Java网络编程精解.pdf

上传人:asd****56 文档编号:70322779 上传时间:2023-01-19 格式:PDF 页数:29 大小:322.22KB
返回 下载 相关 举报
Java网络编程精解.pdf_第1页
第1页 / 共29页
Java网络编程精解.pdf_第2页
第2页 / 共29页
点击查看更多>>
资源描述

《Java网络编程精解.pdf》由会员分享,可在线阅读,更多相关《Java网络编程精解.pdf(29页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、 资料来源:北软教育 www.softedu.org|专业的java 培训、网络培训、网络安全培训基地 在客户/服务器通信模式中,服务器端需要创建监听特定端口的 ServerSocket,ServerSocket 负责接收客户连接请求。本章首先介绍 ServerSocket 类的各个构造方法,以及成员方法的用法,接着介绍服务器如何用多线程来处理与多个客户的通信任务。本章提供线程池的一种实现方式。线程池包括一个工作队列和若干工作线程。服务器程序向工作队列中加入与客户通信的任务,工作线程不断从工作队列中取出任务并执行它。本章还介绍了 java.util.concurrent 包中的线程池类的用法,

2、在服务器程序中可以直接使用它们。3.1 构造构造 ServerSocket ServerSocket 的构造方法有以下几种重载形式:l ServerSocket()throws IOException l ServerSocket(int port)throws IOException l ServerSocket(int port,int backlog)throws IOException l ServerSocket(int port,int backlog,InetAddress bindAddr)throws IOException 在以上构造方法中,参数 port 指定服务器要绑定

3、的端口(服务器要监听的端口),参数 backlog 指定客户连接请求队列的长度,参数 bindAddr 指定服务器要绑定的 IP 地址。3.1.1 绑定端口绑定端口 除了第一个不带参数的构造方法以外,其他构造方法都会使服务器与特定端口绑定,该端口由参数 port 指定。例如,以下代码创建了一个与 80 端口绑定的服务器:ServerSocket serverSocket=new ServerSocket(80);如果运行时无法绑定到 80 端口,以上代码会抛出 IOException,更确切地说,是抛出BindException,它是 IOException 的子类。BindException

4、 一般是由以下原因造成的:l 端口已经被其他服务器进程占用;l 在某些操作系统中,如果没有以超级用户的身份来运行服务器程序,那么操作系统不允许服务器绑定到 11023 之间的端口。如果把参数 port 设为 0,表示由操作系统来为服务器分配一个任意可用的端口。由操作系统分配的端口也称为匿名端口。对于多数服务器,会使用明确的端口,而不会使用匿名端口,因为客户程序需要事先知道服务器的端口,才能方便地访问服务器。在某些场合,匿名端口有着特殊的用途,本章 3.4 节会对此作介绍。3.1.2 设定客户连接请求队列的长度设定客户连接请求队列的长度 当服务器进程运行时,可能会同时监听到多个客户的连接请求。例

5、如,每当一个客户进程执行以下代码:Socket socket=new Socket(www.javathinker.org,80);就意味着在远程 www.javathinker.org 主机的 80 端口上,监听到了一个客户的连接请求。管理客户连接请求的任务是由操作系统来完成的。操作系统把这些连接请求存储在一个先进先出的队列中。许多操作系统限定了队列的最大长度,一般为 50。当队列中的连接请求达到了队列的最大容量时,服务器进程所在的主机会拒绝新的连接请求。只有当服务器进程通UnRegistered 资料来源:北软教育 www.softedu.org|专业的java 培训、网络培训、网络安全培

6、训基地 过 ServerSocket 的 accept()方法从队列中取出连接请求,使队列腾出空位时,队列才能继续加入新的连接请求。对于客户进程,如果它发出的连接请求被加入到服务器的队列中,就意味着客户与服务器的连接建立成功,客户进程从 Socket 构造方法中正常返回。如果客户进程发出的连接请求被服务器拒绝,Socket 构造方法就会抛出ConnectionException。ServerSocket 构造方法的 backlog 参数用来显式设置连接请求队列的长度,它将覆盖操作系统限定的队列的最大长度。值得注意的是,在以下几种情况中,仍然会采用操作系统限定的队列的最大长度:l backlog

7、 参数的值大于操作系统限定的队列的最大长度;l backlog 参数的值小于或等于 0;l 在 ServerSocket 构造方法中没有设置 backlog 参数。以下例程3-1的 Client.java和例程 3-2的Server.java用来演示服务器的连接请求队列的特性。例程 3-1 Client.java import .*;public class Client public static void main(String args)throws Exception final int length=100;String host=localhost;int port=8000;So

8、cket sockets=new Socketlength;for(int i=0;ilength;i+)/试图建立 100 次连接 socketsi=new Socket(host,port);System.out.println(第+(i+1)+次连接成功);Thread.sleep(3000);for(int i=0;ilength;i+)socketsi.close();/断开连接 例程 3-2 Server.java import java.io.*;import .*;public class Server private int port=8000;private ServerS

9、ocket serverSocket;public Server()throws IOException serverSocket=new ServerSocket(port,3);/连接请求队列的长度为 3 System.out.println(服务器启动);UnRegistered 资料来源:北软教育 www.softedu.org|专业的java 培训、网络培训、网络安全培训基地 public void service()while(true)Socket socket=null;try socket=serverSocket.accept();/从连接请求队列中取出一个连接 Syste

10、m.out.println(New connection accepted +socket.getInetAddress()+:+socket.getPort();catch(IOException e)e.printStackTrace();finally try if(socket!=null)socket.close();catch(IOException e)e.printStackTrace();public static void main(String args)throws Exception Server server=new Server();Thread.sleep(60

11、000*10);/睡眠 10 分钟 /server.service();Client 试图与 Server 进行 100 次连接。在 Server 类中,把连接请求队列的长度设为 3。这意味着当队列中有了 3 个连接请求时,如果 Client 再请求连接,就会被 Server 拒绝。下面按照以下步骤运行 Server 和 Client 程序。(1)把 Server 类的 main()方法中的“server.service();”这行程序代码注释掉。这使得服务器与 8 000 端口绑定后,永远不会执行 serverSocket.accept()方法。这意味着队列中的连接请求永远不会被取出。先运行

12、 Server 程序,然后再运行 Client 程序,Client 程序的打印结果如下:第 1 次连接成功 第 2 次连接成功 第 3 次连接成功 Exception in thread main .ConnectException:Connection refused:connect at .PlainSocketImpl.socketConnect(Native Method)at .PlainSocketImpl.doConnect(Unknown Source)at .PlainSocketImpl.connectToAddress(Unknown Source)at .PlainSo

13、cketImpl.connect(Unknown Source)at .SocksSocketImpl.connect(Unknown Source)at .Socket.connect(Unknown Source)at .Socket.connect(Unknown Source)at .Socket.(Unknown Source)UnRegistered 资料来源:北软教育 www.softedu.org|专业的java 培训、网络培训、网络安全培训基地 at .Socket.(Unknown Source)at Client.main(Client.java:10)从以上打印结果可以

14、看出,Client 与 Server 在成功地建立了 3 个连接后,就无法再创建其余的连接了,因为服务器的队列已经满了。(2)把 Server 类的 main()方法按如下方式修改:public static void main(String args)throws Exception Server server=new Server();/Thread.sleep(60000*10);/睡眠 10 分钟 server.service();作了以上修改,服务器与 8 000 端口绑定后,就会在一个 while 循环中不断执行serverSocket.accept()方法,该方法从队列中取出连接

15、请求,使得队列能及时腾出空位,以容纳新的连接请求。先运行 Server 程序,然后再运行 Client 程序,Client 程序的打印结果如下:第 1 次连接成功 第 2 次连接成功 第 3 次连接成功 第 100 次连接成功 从以上打印结果可以看出,此时 Client 能顺利与 Server 建立 100 次连接。3.1.3 设定绑定的设定绑定的 IP 地址地址 如果主机只有一个 IP 地址,那么默认情况下,服务器程序就与该 IP 地址绑定。ServerSocket的第 4 个构造方法 ServerSocket(int port,int backlog,InetAddress bindAdd

16、r)有一个bindAddr 参数,它显式指定服务器要绑定的 IP 地址,该构造方法适用于具有多个 IP 地址的主机。假定一个主机有两个网卡,一个网卡用于连接到 Internet,IP 地址为 222.67.5.94,还有一个网卡用于连接到本地局域网,IP 地址为 192.168.3.4。如果服务器仅仅被本地局域网中的客户访问,那么可以按如下方式创建 ServerSocket:ServerSocket serverSocket=new ServerSocket(8000,10,InetAddress.getByName(192.168.3.4);3.1.4 默认构造方法的作用默认构造方法的作用

17、ServerSocket 有一个不带参数的默认构造方法。通过该方法创建的ServerSocket 不与任何端口绑定,接下来还需要通过 bind()方法与特定端口绑定。这个默认构造方法的用途是,允许服务器在绑定到特定端口之前,先设置ServerSocket 的一些选项。因为一旦服务器与特定端口绑定,有些选项就不能再改变了。在以下代码中,先把 ServerSocket 的 SO_REUSEADDR 选项设为 true,然后再把它与 8000端口绑定:ServerSocket serverSocket=new ServerSocket();serverSocket.setReuseAddress(t

18、rue);/设置 ServerSocket 的选项 UnRegistered 资料来源:北软教育 www.softedu.org|专业的java 培训、网络培训、网络安全培训基地 serverSocket.bind(new InetSocketAddress(8000);/与 8000 端口绑定 如果把以上程序代码改为:ServerSocket serverSocket=new ServerSocket(8000);serverSocket.setReuseAddress(true);/设置 ServerSocket 的选项 那么 serverSocket.setReuseAddress(tr

19、ue)方法就不起任何作用了,因为 SO_ REUSEADDR选项必须在服务器绑定端口之前设置才有效。3.2 接收和关闭与客户的连接接收和关闭与客户的连接 ServerSocket 的 accept()方法从连接请求队列中取出一个客户的连接请求,然后创建与客户连接的 Socket 对象,并将它返回。如果队列中没有连接请求,accept()方法就会一直等待,直到接收到了连接请求才返回。接下来,服务器从 Socket 对象中获得输入流和输出流,就能与客户交换数据。当服务器正在进行发送数据的操作时,如果客户端断开了连接,那么服务器端会抛出一个 IOException的子类 SocketExceptio

20、n 异常:.SocketException:Connection reset by peer 这只是服务器与单个客户通信中出现的异常,这种异常应该被捕获,使得服务器能继续与其他客户通信。以下程序显示了单线程服务器采用的通信流程:public void service()while(true)Socket socket=null;try socket=serverSocket.accept();/从连接请求队列中取出一个连接 System.out.println(New connection accepted +socket.getInetAddress()+:+socket.getPort()

21、;/接收和发送数据 catch(IOException e)/这只是与单个客户通信时遇到的异常,可能是由于客户端过早断开连接引起的 /这种异常不应该中断整个 while 循环 e.printStackTrace();finally try if(socket!=null)socket.close();/与一个客户通信结束后,要关闭Socket catch(IOException e)e.printStackTrace();与单个客户通信的代码放在一个 try 代码块中,如果遇到异常,该异常被 catch 代码块捕获。try 代码块后面还有一个 finally 代码块,它保证不管与客户通信正常结

22、束还是异常结束,最后都会关闭 Socket,断开与这个客户的连接。UnRegistered 资料来源:北软教育 www.softedu.org|专业的java 培训、网络培训、网络安全培训基地 3.3 关闭关闭 ServerSocket ServerSocket 的 close()方法使服务器释放占用的端口,并且断开与所有客户的连接。当一个服务器程序运行结束时,即使没有执行 ServerSocket 的 close()方法,操作系统也会释放这个服务器占用的端口。因此,服务器程序并不一定要在结束之前执行 ServerSocket 的 close()方法。在某些情况下,如果希望及时释放服务器的端口

23、,以便让其他程序能占用该端口,则可以显式调用 ServerSocket 的 close()方法。例如,以下代码用于扫描 165535 之间的端口号。如果 ServerSocket 成功创建,意味着该端口未被其他服务器进程绑定,否者说明该端口已经被其他进程占用:for(int port=1;portjava RandomPort 监听的端口为:3000 C:chapter03classesjava RandomPort 监听的端口为:3004 C:chapter03classesjava RandomPort 监听的端口为:3005 多数服务器会监听固定的端口,这样才便于客户程序访问服务器。匿名

24、端口一般适用于服务器与客户之间的临时通信,通信结束,就断开连接,并且 ServerSocket 占用的临时端口也被释放。FTP(文件传输)协议就使用了匿名端口。如图 3-1 所示,FTP 协议用于在本地文件系统与远程文件系统之间传送文件。图 3-1 FTP 协议用于在本地文件系统与远程文件系统之间传送文件 FTP 使用两个并行的 TCP 连接:一个是控制连接,一个是数据连接。控制连接用于在客户和服务器之间发送控制信息,如用户名和口令、改变远程目录的命令或上传和下载文件的命令。数据连接用于传送文件。TCP 服务器在 21 端口上监听控制连接,如果有客户要求上传或下载文件,就另外建立一个数据连接,

25、通过它来传送文件。数据连接的建立有两种方式。(1)如图 3-2 所示,TCP 服务器在 20 端口上监听数据连接,TCP 客户主动请求建立与该端口的连接。图 3-2 TCP 服务器在 20 端口上监听数据连接(2)如图 3-3 所示,首先由 TCP 客户创建一个监听匿名端口的 ServerSocket,再把这个 ServerSocket 监听的端口号(调用 ServerSocket 的 getLocalPort()方法就能得到端口号)发送给 TCP 服务器,然后由 TCP 服务器主动请求建立与客户端的连接。UnRegistered 资料来源:北软教育 www.softedu.org|专业的ja

26、va 培训、网络培训、网络安全培训基地 图 3-3 TCP 客户在匿名端口上监听数据连接 以上第二种方式就使用了匿名端口,并且是在客户端使用的,用于和服务器建立临时的数据连接。在实际应用中,在服务器端也可以使用匿名端口。3.5 ServerSocket 选项选项 ServerSocket 有以下 3 个选项。l SO_TIMEOUT:表示等待客户连接的超时时间。l SO_REUSEADDR:表示是否允许重用服务器所绑定的地址。l SO_RCVBUF:表示接收数据的缓冲区的大小。3.5.1 SO_TIMEOUT 选项选项 l 设置该选项:public void setSoTimeout(int

27、timeout)throws SocketException l 读取该选项:public int getSoTimeout()throws IOException SO_TIMEOUT 表示 ServerSocket 的 accept()方法等待客户连接的超时时间,以毫秒为单位。如果 SO_TIMEOUT 的值为 0,表示永远不会超时,这是 SO_TIMEOUT 的默认值。当服务器执行 ServerSocket 的 accept()方法时,如果连接请求队列为空,服务器就会一直等待,直到接收到了客户连接才从 accept()方法返回。如果设定了超时时间,那么当服务器等待的时间超过了超时时间,就

28、会抛出SocketTimeoutException,它是 InterruptedException 的子类。如例程 3-4 所示的 TimeoutTester 把超时时间设为 6 秒钟。例程 3-4 TimeoutTester.java import java.io.*;import .*;public class TimeoutTester public static void main(String args)throws IOException ServerSocket serverSocket=new ServerSocket(8000);serverSocket.setSoTimeo

29、ut(6000);/等待客户连接的时间不超过 6 秒 Socket socket=serverSocket.accept();socket.close();System.out.println(服务器关闭);运行以上程序,过 6 秒钟后,程序会从 serverSocket.accept()方法中抛出 Socket-TimeoutException:C:chapter03classesjava TimeoutTester Exception in thread main .SocketTimeoutException:Accept timed out at .PlainSocketImpl.so

30、cketAccept(Native Method)UnRegistered 资料来源:北软教育 www.softedu.org|专业的java 培训、网络培训、网络安全培训基地 at .PlainSocketImpl.accept(Unknown Source)at .ServerSocket.implAccept(Unknown Source)at .ServerSocket.accept(Unknown Source)at TimeoutTester.main(TimeoutTester.java:8)如果把程序中的“serverSocket.setSoTimeout(6000)”注释掉,

31、那么 serverSocket.accept()方法永远不会超时,它会一直等待下去,直到接收到了客户的连接,才会从 accept()方法返回。服务器执行 serverSocket.accept()方法时,等待客户连接的过程也称为阻塞。本书第 4 章的 4.1 节(线程阻塞的概念)详细介绍了阻塞的概念。3.5.2 SO_REUSEADDR 选项选项 l 设置该选项:public void setResuseAddress(boolean on)throws SocketException l 读取该选项:public boolean getResuseAddress()throws Socket

32、Exception 这个选项与 Socket 的 SO_REUSEADDR 选项相同,用于决定如果网络上仍然有数据向旧的 ServerSocket 传输数据,是否允许新的 ServerSocket 绑定到与旧的ServerSocket 同样的端口上。SO_REUSEADDR 选项的默认值与操作系统有关,在某些操作系统中,允许重用端口,而在某些操作系统中不允许重用端口。当 ServerSocket 关闭时,如果网络上还有发送到这个 ServerSocket 的数据,这个 ServerSocket 不会立刻释放本地端口,而是会等待一段时间,确保接收到了网络上发送过来的延迟数据,然后再释放端口。许多

33、服务器程序都使用固定的端口。当服务器程序关闭后,有可能它的端口还会被占用一段时间,如果此时立刻在同一个主机上重启服务器程序,由于端口已经被占用,使得服务器程序无法绑定到该端口,服务器启动失败,并抛出 BindException:Exception in thread main .BindException:Address already in use:JVM_Bind 为了确保一个进程关闭了 ServerSocket 后,即使操作系统还没释放端口,同一个主机上的其他进程还可以立刻重用该端口,可以调用 ServerSocket 的 setResuse-Address(true)方法:if(!se

34、rverSocket.getResuseAddress()serverSocket.setResuseAddress(true);值得注意的是,serverSocket.setResuseAddress(true)方法必须在 ServerSocket 还没有绑定到一个本地端口之前调用,否则执行 serverSocket.setResuseAddress(true)方法无效。此外,两个共用同一个端口的进程必须都调用 serverSocket.setResuseAddress(true)方法,才能使得一个进程关闭 ServerSocket 后,另一个进程的 ServerSocket 还能够立刻重用

35、相同端口。3.5.3 SO_RCVBUF 选项选项 l 设置该选项:public void setReceiveBufferSize(int size)throws SocketException l 读取该选项:public int getReceiveBufferSize()throws SocketException SO_RCVBUF 表示服务器端的用于接收数据的缓冲区的大小,以字节为单位。一般说来,传输大的连续的数据块(基于 HTTP 或 FTP 协议的数据传输)可以使用较大的缓冲区,这可以减少传输数据的次数,从而提高传输数据的效率。而对于交互式的通信(Telnet 和网络游戏),则

36、应该采用小的缓冲区,确保能及时把小批量的数据发送给对方。SO_RCVBUF 的默认值与操作系统有关。例如,在 Windows 2000 中运行以下代码时,显示SO_RCVBUF 的默认值为 8192:UnRegistered 资料来源:北软教育 www.softedu.org|专业的java 培训、网络培训、网络安全培训基地 ServerSocket serverSocket=new ServerSocket(8000);System.out.println(serverSocket.getReceiveBufferSize();/打印 8192 无论在 ServerSocket 绑定到特定端

37、口之前或之后,调用 setReceiveBufferSize()方法都有效。例外情况下是如果要设置大于 64K 的缓冲区,则必须在 ServerSocket 绑定到特定端口之前进行设置才有效。例如,以下代码把缓冲区设为 128K:ServerSocket serverSocket=new ServerSocket();int size=serverSocket.getReceiveBufferSize();if(size131072)serverSocket.setReceiveBufferSize(131072);/把缓冲区的大小设为 128K serverSocket.bind(new I

38、netSocketAddress(8000);/与 8 000 端口绑定 执行 serverSocket.setReceiveBufferSize()方法,相当于对所有由 serverSocket.accept()方法返回的 Socket 设置接收数据的缓冲区的大小。3.5.4 设定连接时间设定连接时间、延迟和带宽的相对重要性延迟和带宽的相对重要性 l public void setPerformancePreferences(int connectionTime,int latency,int bandwidth)该方法的作用与 Socket 的 setPerformancePreferen

39、ces()方法的作用相同,用于设定连接时间、延迟和带宽的相对重要性,参见本书第 2 章的 2.5.10 节(设定连接时间、延迟和带宽的相对重要性)。3.6 创建多线程的服务器创建多线程的服务器 在本书第 1 章的 1.5.1 节的例程 1-2 的 EchoServer 中,其 service()方法负责接收客户连接,以及与客户通信。service()方法的处理流程如下:while(true)Socket socket=null;try socket=serverSocket.accept();/接收客户连接 /从 Socket 中获得输入流与输出流,与客户通信 catch(IOExceptio

40、n e)e.printStackTrace();finally try if(socket!=null)socket.close();/断开连接 catch(IOException e)e.printStackTrace();EchoServer 接收到一个客户连接,就与客户进行通信,通信完毕后断开连接,然后再接收下一个客户连接。假如同时有多个客户请求连接,这些客户就必须排队等候 EchoServer 的响应。EchoServer 无法同时与多个客户通信。许多实际应用要求服务器具有同时为多个客户提供服务的能力。HTTP 服务器就是最明显的UnRegistered 资料来源:北软教育 www.s

41、oftedu.org|专业的java 培训、网络培训、网络安全培训基地 例子。任何时刻,HTTP 服务器都可能接收到大量的客户请求,每个客户都希望能快速得到HTTP 服务器的响应。如果长时间让客户等待,会使网站失去信誉,从而降低访问量。可以用并发性能来衡量一个服务器同时响应多个客户的能力。一个具有好的并发性能的服务器,必须符合两个条件:l 能同时接收并处理多个客户连接;l 对于每个客户,都会迅速给予响应。服务器同时处理的客户连接数目越多,并且对每个客户作出响应的速度越快,就表明并发性能越高。用多个线程来同时为多个客户提供服务,这是提高服务器的并发性能的最常用的手段。本节将按照 3 种方式来重新

42、实现 EchoServer,它们都使用了多线程。l 为每个客户分配一个工作线程。l 创建一个线程池,由其中的工作线程来为客户服务。l 利用 JDK 的 Java 类库中现成的线程池,由它的工作线程来为客户服务。3.6.1 为每个客户分配一个线程为每个客户分配一个线程 服务器的主线程负责接收客户的连接,每次接收到一个客户连接,就会创建一个工作线程,由它负责与客户的通信。以下是 EchoServer 的 service()方法的代码:public void service()while(true)Socket socket=null;try socket=serverSocket.accept()

43、;/接收客户连接 Thread workThread=new Thread(new Handler(socket);/创建一个工作线程 workThread.start();/启动工作线程 catch(IOException e)e.printStackTrace();以上工作线程 workThread 执行 Handler 的 run()方法。Handler 类实现了 Runnable 接口,它的 run()方法负责与单个客户通信,与客户通信结束后,就会断开连接,执行 Handler 的run()方法的工作线程也会自然终止。如例程 3-5 所示是 EchoServer 类及 Handler

44、类的源程序。例程 3-5 EchoServer.java(为每个任务分配一个线程)package multithread1;import java.io.*;import .*;public class EchoServer private int port=8000;private ServerSocket serverSocket;public EchoServer()throws IOException UnRegistered 资料来源:北软教育 www.softedu.org|专业的java 培训、网络培训、网络安全培训基地 serverSocket=new ServerSocket

45、(port);System.out.println(服务器启动);public void service()while(true)Socket socket=null;try socket=serverSocket.accept();/接收客户连接 Thread workThread=new Thread(new Handler(socket);/创建一个工作线程 workThread.start();/启动工作线程 catch(IOException e)e.printStackTrace();public static void main(String args)throws IOExce

46、ption new EchoServer().service();class Handler implements Runnable /负责与单个客户的通信 private Socket socket;public Handler(Socket socket)this.socket=socket;private PrintWriter getWriter(Socket socket)throws IOException private BufferedReader getReader(Socket socket)throws IOException public String echo(Str

47、ing msg)public void run()try System.out.println(New connection accepted +socket.getInetAddress()+:+socket.getPort();BufferedReader br=getReader(socket);PrintWriter pw=getWriter(socket);String msg=null;while(msg=br.readLine()!=null)/接收和发送数据,直到通信结束 System.out.println(msg);pw.println(echo(msg);if(msg.e

48、quals(bye)break;UnRegistered 资料来源:北软教育 www.softedu.org|专业的java 培训、网络培训、网络安全培训基地 catch(IOException e)e.printStackTrace();finally try if(socket!=null)socket.close();/断开连接 catch(IOException e)e.printStackTrace();3.6.2 创建线程池创建线程池 在 3.6.1 节介绍的实现方式中,对每个客户都分配一个新的工作线程。当工作线程与客户通信结束,这个线程就被销毁。这种实现方式有以下不足之处。l 服

49、务器创建和销毁工作线程的开销(包括所花费的时间和系统资源)很大。如果服务器需要与许多客户通信,并且与每个客户的通信时间都很短,那么有可能服务器为客户创建新线程的开销比实际与客户通信的开销还要大。l 除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。每个线程本身都会占用一定的内存(每个线程需要大约 1M 内存),如果同时有大量客户连接服务器,就必须创建大量工作线程,它们消耗了大量内存,可能会导致系统的内存空间不足。l 如果线程数目固定,并且每个线程都有很长的生命周期,那么线程切换也是相对固定的。不同操作系统有不同的切换周期,一般在 20 毫秒左右。这里所说的线程切换是指在 Java虚拟机,

50、以及底层操作系统的调度下,线程之间转让 CPU 的使用权。如果频繁创建和销毁线程,那么将导致频繁地切换线程,因为一个线程被销毁后,必然要把 CPU 转让给另一个已经就绪的线程,使该线程获得运行机会。在这种情况下,线程之间的切换不再遵循系统的固定切换周期,切换线程的开销甚至比创建及销毁线程的开销还大。线程池为线程生命周期开销问题和系统资源不足问题提供了解决方案。线程池中预先创建了一些工作线程,它们不断从工作队列中取出任务,然后执行该任务。当工作线程执行完一个任务时,就会继续执行工作队列中的下一个任务。线程池具有以下优点:l 减少了创建和销毁线程的次数,每个工作线程都可以一直被重用,能执行多个任务

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com