Java網(wǎng)絡編程精解——孫衛(wèi)琴.doc_第1頁
Java網(wǎng)絡編程精解——孫衛(wèi)琴.doc_第2頁
Java網(wǎng)絡編程精解——孫衛(wèi)琴.doc_第3頁
Java網(wǎng)絡編程精解——孫衛(wèi)琴.doc_第4頁
Java網(wǎng)絡編程精解——孫衛(wèi)琴.doc_第5頁
已閱讀5頁,還剩25頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

在客戶/服務器通信模式中,服務器端需要創(chuàng)建監(jiān)聽特定端口的ServerSocket,ServerSocket負責接收客戶連接請求。本章首先介紹ServerSocket類的各個構造方法,以及成員方法的用法,接著介紹服務器如何用多線程來處理與多個客戶的通信任務。本章提供線程池的一種實現(xiàn)方式。線程池包括一個工作隊列和若干工作線程。服務器程序向工作隊列中加入與客戶通信的任務,工作線程不斷從工作隊列中取出任務并執(zhí)行它。本章還介紹了java.util.concurrent包中的線程池類的用法,在服務器程序中可以直接使用它們。3.1 構造ServerSocketServerSocket的構造方法有以下幾種重載形式:l ServerSocket()throws IOExceptionl ServerSocket(int port) throws IOExceptionl ServerSocket(int port, int backlog) throws IOExceptionl ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException在以上構造方法中,參數(shù)port指定服務器要綁定的端口(服務器要監(jiān)聽的端口),參數(shù)backlog指定客戶連接請求隊列的長度,參數(shù)bindAddr指定服務器要綁定的IP地址。3.1.1 綁定端口除了第一個不帶參數(shù)的構造方法以外,其他構造方法都會使服務器與特定端口綁定,該端口由參數(shù)port指定。例如,以下代碼創(chuàng)建了一個與80端口綁定的服務器:ServerSocket serverSocket=new ServerSocket(80);如果運行時無法綁定到80端口,以上代碼會拋出IOException,更確切地說,是拋出BindException,它是IOException的子類。BindException一般是由以下原因造成的:l 端口已經(jīng)被其他服務器進程占用;l 在某些操作系統(tǒng)中,如果沒有以超級用戶的身份來運行服務器程序,那么操作系統(tǒng)不允許服務器綁定到11023之間的端口。如果把參數(shù)port設為0,表示由操作系統(tǒng)來為服務器分配一個任意可用的端口。由操作系統(tǒng)分配的端口也稱為匿名端口。對于多數(shù)服務器,會使用明確的端口,而不會使用匿名端口,因為客戶程序需要事先知道服務器的端口,才能方便地訪問服務器。在某些場合,匿名端口有著特殊的用途,本章3.4節(jié)會對此作介紹。3.1.2 設定客戶連接請求隊列的長度當服務器進程運行時,可能會同時監(jiān)聽到多個客戶的連接請求。例如,每當一個客戶進程執(zhí)行以下代碼:Socket socket=new Socket(,80);就意味著在遠程主機的80端口上,監(jiān)聽到了一個客戶的連接請求。管理客戶連接請求的任務是由操作系統(tǒng)來完成的。操作系統(tǒng)把這些連接請求存儲在一個先進先出的隊列中。許多操作系統(tǒng)限定了隊列的最大長度,一般為50。當隊列中的連接請求達到了隊列的最大容量時,服務器進程所在的主機會拒絕新的連接請求。只有當服務器進程通過ServerSocket的accept()方法從隊列中取出連接請求,使隊列騰出空位時,隊列才能繼續(xù)加入新的連接請求。對于客戶進程,如果它發(fā)出的連接請求被加入到服務器的隊列中,就意味著客戶與服務器的連接建立成功,客戶進程從Socket構造方法中正常返回。如果客戶進程發(fā)出的連接請求被服務器拒絕,Socket構造方法就會拋出ConnectionException。ServerSocket構造方法的backlog參數(shù)用來顯式設置連接請求隊列的長度,它將覆蓋操作系統(tǒng)限定的隊列的最大長度。值得注意的是,在以下幾種情況中,仍然會采用操作系統(tǒng)限定的隊列的最大長度:l backlog參數(shù)的值大于操作系統(tǒng)限定的隊列的最大長度;l backlog參數(shù)的值小于或等于0;l 在ServerSocket構造方法中沒有設置backlog參數(shù)。以下例程3-1的Client.java和例程3-2的Server.java用來演示服務器的連接請求隊列的特性。例程3-1 Client.javaimport .*;public class Client public static void main(String args)throws Exception final int length=100; String host=localhost; int port=8000; Socket 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.javaimport java.io.*;import .*;public class Server private int port=8000; private ServerSocket serverSocket; public Server() throws IOException serverSocket = new ServerSocket(port,3); /連接請求隊列的長度為3 System.out.println(服務器啟動); public void service() while (true) Socket socket=null; try socket = serverSocket.accept(); /從連接請求隊列中取出一個連接 System.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(60000*10); /睡眠10分鐘 /server.service(); Client試圖與Server進行100次連接。在Server類中,把連接請求隊列的長度設為3。這意味著當隊列中有了3個連接請求時,如果Client再請求連接,就會被Server拒絕。下面按照以下步驟運行Server和Client程序。(1)把Server類的main()方法中的“server.service();”這行程序代碼注釋掉。這使得服務器與8 000端口綁定后,永遠不會執(zhí)行serverSocket.accept()方法。這意味著隊列中的連接請求永遠不會被取出。先運行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 .PlainSocketImpl.connect(Unknown Source) at .SocksSocketImpl.connect(Unknown Source) at .Socket.connect(Unknown Source) at .Socket.connect(Unknown Source) at .Socket.(Unknown Source) at .Socket.(Unknown Source) at Client.main(Client.java:10)從以上打印結果可以看出,Client與Server在成功地建立了3個連接后,就無法再創(chuàng)建其余的連接了,因為服務器的隊列已經(jīng)滿了。(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循環(huán)中不斷執(zhí)行serverSocket.accept()方法,該方法從隊列中取出連接請求,使得隊列能及時騰出空位,以容納新的連接請求。先運行Server程序,然后再運行Client程序,Client程序的打印結果如下:第1次連接成功第2次連接成功第3次連接成功第100次連接成功從以上打印結果可以看出,此時Client能順利與Server建立100次連接。3.1.3 設定綁定的IP地址如果主機只有一個IP地址,那么默認情況下,服務器程序就與該IP地址綁定。ServerSocket的第4個構造方法ServerSocket(int port, int backlog, InetAddress bindAddr)有一個bindAddr參數(shù),它顯式指定服務器要綁定的IP地址,該構造方法適用于具有多個IP地址的主機。假定一個主機有兩個網(wǎng)卡,一個網(wǎng)卡用于連接到Internet, IP地址為4,還有一個網(wǎng)卡用于連接到本地局域網(wǎng),IP地址為。如果服務器僅僅被本地局域網(wǎng)中的客戶訪問,那么可以按如下方式創(chuàng)建ServerSocket:ServerSocket serverSocket=new ServerSocket(8000,10,InetAddress.getByName ();3.1.4 默認構造方法的作用ServerSocket有一個不帶參數(shù)的默認構造方法。通過該方法創(chuàng)建的ServerSocket不與任何端口綁定,接下來還需要通過bind()方法與特定端口綁定。這個默認構造方法的用途是,允許服務器在綁定到特定端口之前,先設置ServerSocket的一些選項。因為一旦服務器與特定端口綁定,有些選項就不能再改變了。在以下代碼中,先把ServerSocket的SO_REUSEADDR選項設為true,然后再把它與8000端口綁定:ServerSocket serverSocket=new ServerSocket();serverSocket.setReuseAddress(true); /設置ServerSocket的選項serverSocket.bind(new InetSocketAddress(8000); /與8000端口綁定如果把以上程序代碼改為:ServerSocket serverSocket=new ServerSocket(8000);serverSocket.setReuseAddress(true); /設置ServerSocket的選項那么serverSocket.setReuseAddress(true)方法就不起任何作用了,因為SO_ REUSEADDR選項必須在服務器綁定端口之前設置才有效。3.2 接收和關閉與客戶的連接ServerSocket的accept()方法從連接請求隊列中取出一個客戶的連接請求,然后創(chuàng)建與客戶連接的Socket對象,并將它返回。如果隊列中沒有連接請求,accept()方法就會一直等待,直到接收到了連接請求才返回。接下來,服務器從Socket對象中獲得輸入流和輸出流,就能與客戶交換數(shù)據(jù)。當服務器正在進行發(fā)送數(shù)據(jù)的操作時,如果客戶端斷開了連接,那么服務器端會拋出一個IOException的子類SocketException異常:.SocketException: Connection reset by peer這只是服務器與單個客戶通信中出現(xiàn)的異常,這種異常應該被捕獲,使得服務器能繼續(xù)與其他客戶通信。以下程序顯示了單線程服務器采用的通信流程:public void service() while (true) Socket socket=null;try socket = serverSocket.accept(); /從連接請求隊列中取出一個連接 System.out.println(New connection accepted + socket.getInetAddress() + : +socket.getPort(); /接收和發(fā)送數(shù)據(jù) catch (IOException e) /這只是與單個客戶通信時遇到的異常,可能是由于客戶端過早斷開連接引起的 /這種異常不應該中斷整個while循環(huán) e.printStackTrace(); finally tryif(socket!=null)socket.close(); /與一個客戶通信結束后,要關閉Socket catch (IOException e) e.printStackTrace(); 與單個客戶通信的代碼放在一個try代碼塊中,如果遇到異常,該異常被catch代碼塊捕獲。try代碼塊后面還有一個finally代碼塊,它保證不管與客戶通信正常結束還是異常結束,最后都會關閉Socket,斷開與這個客戶的連接。3.3 關閉ServerSocketServerSocket的close()方法使服務器釋放占用的端口,并且斷開與所有客戶的連接。當一個服務器程序運行結束時,即使沒有執(zhí)行ServerSocket的close()方法,操作系統(tǒng)也會釋放這個服務器占用的端口。因此,服務器程序并不一定要在結束之前執(zhí)行ServerSocket的close()方法。在某些情況下,如果希望及時釋放服務器的端口,以便讓其他程序能占用該端口,則可以顯式調(diào)用ServerSocket的close()方法。例如,以下代碼用于掃描165535之間的端口號。如果ServerSocket成功創(chuàng)建,意味著該端口未被其他服務器進程綁定,否者說明該端口已經(jīng)被其他進程占用:for(int port=1;portjava RandomPort監(jiān)聽的端口為:3000C:chapter03classesjava RandomPort監(jiān)聽的端口為:3004C:chapter03classesjava RandomPort監(jiān)聽的端口為:3005多數(shù)服務器會監(jiān)聽固定的端口,這樣才便于客戶程序訪問服務器。匿名端口一般適用于服務器與客戶之間的臨時通信,通信結束,就斷開連接,并且ServerSocket占用的臨時端口也被釋放。FTP(文件傳輸)協(xié)議就使用了匿名端口。如圖3-1所示,F(xiàn)TP協(xié)議用于在本地文件系統(tǒng)與遠程文件系統(tǒng)之間傳送文件。圖3-1 FTP協(xié)議用于在本地文件系統(tǒng)與遠程文件系統(tǒng)之間傳送文件FTP使用兩個并行的TCP連接:一個是控制連接,一個是數(shù)據(jù)連接??刂七B接用于在客戶和服務器之間發(fā)送控制信息,如用戶名和口令、改變遠程目錄的命令或上傳和下載文件的命令。數(shù)據(jù)連接用于傳送文件。TCP服務器在21端口上監(jiān)聽控制連接,如果有客戶要求上傳或下載文件,就另外建立一個數(shù)據(jù)連接,通過它來傳送文件。數(shù)據(jù)連接的建立有兩種方式。(1)如圖3-2所示,TCP服務器在20端口上監(jiān)聽數(shù)據(jù)連接,TCP客戶主動請求建立與該端口的連接。圖3-2 TCP服務器在20端口上監(jiān)聽數(shù)據(jù)連接(2)如圖3-3所示,首先由TCP客戶創(chuàng)建一個監(jiān)聽匿名端口的ServerSocket,再把這個ServerSocket監(jiān)聽的端口號(調(diào)用ServerSocket的getLocalPort()方法就能得到端口號)發(fā)送給TCP服務器,然后由TCP服務器主動請求建立與客戶端的連接。圖3-3 TCP客戶在匿名端口上監(jiān)聽數(shù)據(jù)連接以上第二種方式就使用了匿名端口,并且是在客戶端使用的,用于和服務器建立臨時的數(shù)據(jù)連接。在實際應用中,在服務器端也可以使用匿名端口。3.5 ServerSocket選項ServerSocket有以下3個選項。l SO_TIMEOUT:表示等待客戶連接的超時時間。l SO_REUSEADDR:表示是否允許重用服務器所綁定的地址。l SO_RCVBUF:表示接收數(shù)據(jù)的緩沖區(qū)的大小。3.5.1 SO_TIMEOUT選項l 設置該選項:public void setSoTimeout(int timeout) throws SocketExceptionl 讀取該選項:public int getSoTimeout () throws IOExceptionSO_TIMEOUT表示ServerSocket的accept()方法等待客戶連接的超時時間,以毫秒為單位。如果SO_TIMEOUT的值為0,表示永遠不會超時,這是SO_TIMEOUT的默認值。當服務器執(zhí)行ServerSocket的accept()方法時,如果連接請求隊列為空,服務器就會一直等待,直到接收到了客戶連接才從accept()方法返回。如果設定了超時時間,那么當服務器等待的時間超過了超時時間,就會拋出SocketTimeoutException,它是InterruptedException的子類。如例程3-4所示的TimeoutTester把超時時間設為6秒鐘。例程3-4 TimeoutTester.javaimport java.io.*;import .*;public class TimeoutTester public static void main(String args)throws IOException ServerSocket serverSocket=new ServerSocket(8000); serverSocket.setSoTimeout(6000); /等待客戶連接的時間不超過6秒 Socket socket=serverSocket.accept(); socket.close(); System.out.println(服務器關閉); 運行以上程序,過6秒鐘后,程序會從serverSocket.accept()方法中拋出Socket- TimeoutException:C:chapter03classesjava TimeoutTesterException in thread main .SocketTimeoutException: Accept timed out at .PlainSocketImpl.socketAccept(Native Method) at .PlainSocketImpl.accept(Unknown Source) at .ServerSocket.implAccept(Unknown Source) at .ServerSocket.accept(Unknown Source) at TimeoutTester.main(TimeoutTester.java:8)如果把程序中的“serverSocket.setSoTimeout(6000)”注釋掉,那么serverSocket. accept()方法永遠不會超時,它會一直等待下去,直到接收到了客戶的連接,才會從accept()方法返回。服務器執(zhí)行serverSocket.accept()方法時,等待客戶連接的過程也稱為阻塞。本書第4章的4.1節(jié)(線程阻塞的概念)詳細介紹了阻塞的概念。3.5.2 SO_REUSEADDR選項l 設置該選項:public void setResuseAddress(boolean on) throws SocketExceptionl 讀取該選項:public boolean getResuseAddress() throws SocketException這個選項與Socket的SO_REUSEADDR選項相同,用于決定如果網(wǎng)絡上仍然有數(shù)據(jù)向舊的ServerSocket傳輸數(shù)據(jù),是否允許新的ServerSocket綁定到與舊的ServerSocket同樣的端口上。SO_REUSEADDR選項的默認值與操作系統(tǒng)有關,在某些操作系統(tǒng)中,允許重用端口,而在某些操作系統(tǒng)中不允許重用端口。當ServerSocket關閉時,如果網(wǎng)絡上還有發(fā)送到這個ServerSocket的數(shù)據(jù),這個ServerSocket不會立刻釋放本地端口,而是會等待一段時間,確保接收到了網(wǎng)絡上發(fā)送過來的延遲數(shù)據(jù),然后再釋放端口。許多服務器程序都使用固定的端口。當服務器程序關閉后,有可能它的端口還會被占用一段時間,如果此時立刻在同一個主機上重啟服務器程序,由于端口已經(jīng)被占用,使得服務器程序無法綁定到該端口,服務器啟動失敗,并拋出BindException:Exception in thread main .BindException: Address already in use: JVM_Bind為了確保一個進程關閉了ServerSocket后,即使操作系統(tǒng)還沒釋放端口,同一個主機上的其他進程還可以立刻重用該端口,可以調(diào)用ServerSocket的setResuse- Address(true)方法:if(!serverSocket.getResuseAddress()serverSocket.setResuseAddress(true);值得注意的是,serverSocket.setResuseAddress(true)方法必須在ServerSocket還沒有綁定到一個本地端口之前調(diào)用,否則執(zhí)行serverSocket.setResuseAddress(true)方法無效。此外,兩個共用同一個端口的進程必須都調(diào)用serverSocket.setResuseAddress(true)方法,才能使得一個進程關閉ServerSocket后,另一個進程的ServerSocket還能夠立刻重用相同端口。3.5.3 SO_RCVBUF選項l 設置該選項:public void setReceiveBufferSize(int size) throws SocketExceptionl 讀取該選項:public int getReceiveBufferSize() throws SocketExceptionSO_RCVBUF表示服務器端的用于接收數(shù)據(jù)的緩沖區(qū)的大小,以字節(jié)為單位。一般說來,傳輸大的連續(xù)的數(shù)據(jù)塊(基于HTTP或FTP協(xié)議的數(shù)據(jù)傳輸)可以使用較大的緩沖區(qū),這可以減少傳輸數(shù)據(jù)的次數(shù),從而提高傳輸數(shù)據(jù)的效率。而對于交互式的通信(Telnet和網(wǎng)絡游戲),則應該采用小的緩沖區(qū),確保能及時把小批量的數(shù)據(jù)發(fā)送給對方。SO_RCVBUF的默認值與操作系統(tǒng)有關。例如,在Windows 2000中運行以下代碼時,顯示SO_RCVBUF的默認值為8192:ServerSocket serverSocket=new ServerSocket(8000);System.out.println(serverSocket.getReceiveBufferSize(); /打印8192無論在ServerSocket綁定到特定端口之前或之后,調(diào)用setReceiveBufferSize()方法都有效。例外情況下是如果要設置大于64K的緩沖區(qū),則必須在ServerSocket綁定到特定端口之前進行設置才有效。例如,以下代碼把緩沖區(qū)設為128K:ServerSocket serverSocket=new ServerSocket();int size=serverSocket.getReceiveBufferSize();if(size131072) serverSocket.setReceiveBufferSize(131072); /把緩沖區(qū)的大小設為128KserverSocket.bind(new InetSocketAddress(8000); /與8 000端口綁定執(zhí)行serverSocket.setReceiveBufferSize()方法,相當于對所有由serverSocket.accept()方法返回的Socket設置接收數(shù)據(jù)的緩沖區(qū)的大小。3.5.4 設定連接時間、延遲和帶寬的相對重要性l public void setPerformancePreferences(int connectionTime,int latency,int bandwidth)該方法的作用與Socket的setPerformancePreferences()方法的作用相同,用于設定連接時間、延遲和帶寬的相對重要性,參見本書第2章的2.5.10節(jié)(設定連接時間、延遲和帶寬的相對重要性)。3.6 創(chuàng)建多線程的服務器在本書第1章的1.5.1節(jié)的例程1-2的EchoServer中,其service()方法負責接收客戶連接,以及與客戶通信。service()方法的處理流程如下:while (true) Socket socket=null; try socket = serverSocket.accept(); /接收客戶連接 /從Socket中獲得輸入流與輸出流,與客戶通信 catch (IOException e) e.printStackTrace(); finally tryif(socket!=null)socket.close(); /斷開連接 catch (IOException e) e.printStackTrace(); EchoServer接收到一個客戶連接,就與客戶進行通信,通信完畢后斷開連接,然后再接收下一個客戶連接。假如同時有多個客戶請求連接,這些客戶就必須排隊等候EchoServer的響應。EchoServer無法同時與多個客戶通信。許多實際應用要求服務器具有同時為多個客戶提供服務的能力。HTTP服務器就是最明顯的例子。任何時刻,HTTP服務器都可能接收到大量的客戶請求,每個客戶都希望能快速得到HTTP服務器的響應。如果長時間讓客戶等待,會使網(wǎng)站失去信譽,從而降低訪問量。可以用并發(fā)性能來衡量一個服務器同時響應多個客戶的能力。一個具有好的并發(fā)性能的服務器,必須符合兩個條件:l 能同時接收并處理多個客戶連接;l 對于每個客戶,都會迅速給予響應。服務器同時處理的客戶連接數(shù)目越多,并且對每個客戶作出響應的速度越快,就表明并發(fā)性能越高。用多個線程來同時為多個客戶提供服務,這是提高服務器的并發(fā)性能的最常用的手段。本節(jié)將按照3種方式來重新實現(xiàn)EchoServer,它們都使用了多線程。l 為每個客戶分配一個工作線程。l 創(chuàng)建一個線程池,由其中的工作線程來為客戶服務。l 利用JDK的Java類庫中現(xiàn)成的線程池,由它的工作線程來為客戶服務。3.6.1 為每個客戶分配一個線程服務器的主線程負責接收客戶的連接,每次接收到一個客戶連接,就會創(chuàng)建一個工作線程,由它負責與客戶的通信。以下是EchoServer的service()方法的代碼:public void service() while (true) Socket socket=null; try socket = serverSocket.accept(); /接收客戶連接 Thread workThread=new Thread(new Handler(socket); /創(chuàng)建一個工作線程 workThread.start(); /啟動工作線程 catch (IOException e) e.printStackTrace(); 以上工作線程workThread執(zhí)行Handler的run()方法。Handler類實現(xiàn)了Runnable接口,它的run()方法負責與單個客戶通信,與客戶通信結束后,就會斷開連接,執(zhí)行Handler的run()方法的工作線程也會自然終止。如例程3-5所示是EchoServer類及Handler類的源程序。例程3-5 EchoServer.java(為每個任務分配一個線程)package multithread1;import java.io.*;import .*;public class EchoServer private int port=8000; private ServerSocket serverSocket; public EchoServer() throws IOException serverSocket = new ServerSocket(port); System.out.println(服務器啟動); public void service() while (true) Socket socket=null; try socket = serverSocket.accept(); /接收客戶連接 Thread workThread=new Thread(new Handler(socket); /創(chuàng)建一個工作線程 workThread.start(); /啟動工作線程 catch (IOException e) e.printStackTrace(); public static void main(String args)throws IOException 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(String 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) /接收和發(fā)送數(shù)據(jù),直到通信結束 System.out.println(msg); pw.println(echo(msg); if (msg.equals(bye) break; catch (IOException e) e.printStackTrace(); finally try if(socket!=null)socket.close(); /斷開連接 catch (IOException e) e.printStackTrace(); 3.6.2 創(chuàng)建線程池在3.6.1節(jié)介紹的實現(xiàn)方式中,對每個客戶都分配一個新的工作線程。當工作線程與客戶通信結束,這個線程就被銷毀。這種實現(xiàn)方式有以下不足之處。l 服務器創(chuàng)建和銷毀工作線程的開銷(包括所花費的時間和系統(tǒng)資源)很大。如果服務器需要與許多客戶通信,并且與每個客戶的通信時間都很短,那么有可能服務器為客戶創(chuàng)建新線程的開銷比實際與客戶通信的開銷還要大。l 除了創(chuàng)建和銷毀線程的開銷之外,活動的線程也消耗系統(tǒng)資源。每個線程本身都會占用一定的內(nèi)存(每個線程需要大約1M內(nèi)存),如果同時有大量客戶連接服務器,就必須創(chuàng)建大量工作線程,它們消耗了大量內(nèi)存,可能會導致系統(tǒng)的內(nèi)存空間不足。l 如果線程數(shù)目固定,并且每個線程都有很長的生命周期,那么線程切換也是相對固定的。不同操作系統(tǒng)有不同的切換周期,一般在20毫秒左右。這里所說的線程切換是指在Java虛擬機,以及底層操作系統(tǒng)的調(diào)度下,線程之間轉讓CPU的使用權。如果頻繁創(chuàng)建和銷毀線程,那么將導致頻繁地切換線程,因為一個線程被銷毀后,必然要把CPU轉讓給另一個已經(jīng)就緒的線程,使該線程獲得運行機會。在這種情況下,線程之間的切換不再遵循系統(tǒng)的固定切換周期,切換線程的開銷甚至比創(chuàng)建及銷毀線程的開銷還大。線程池為線程生命周期開銷問題和系統(tǒng)資源不足問題提供了解決方案。線程池中預先創(chuàng)建了一些工作線程,它們不斷從工作隊列中取出任務,然后執(zhí)行該任務。當

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論