首頁 >Java >java教程 >菜鳥初學Java的備忘錄(九)

菜鳥初學Java的備忘錄(九)

黄舟
黄舟原創
2016-12-20 13:55:231070瀏覽


我忽然發現了利用接口實現多線程和利用類構造線程體的不同了,以前我似乎並沒有太多的注重.利用類構造線程體的時候,需要使用這個類來定義線程對象,比如MyThread thread1=New MyThread(),而使用介面建立線程體的時候,只需要用到Thread類別就可以了,比如Thread thread1=new Thread(MyThread).這在幾天前所講的多線程的Socket中均有體現.

前面不懈的努力使我現在可以繼續原先未完成的工作,正式向連接池版本的Socket進發了.

回顧一下我們是如何創建多線程的伺服器MultiThreadRemoteFileServer的,這得看看前幾天的內容了.概括起來,就是為每個等待連接的客戶new一個線程使用,來一個分配一個.每當有客戶機申請一個連接時都在一個新Thread 中創建一個新ConnectionHandler(注:使用介面構造的線程體).這意味著可能有一打Thread在整個系統中運行著.顯然,系統的開銷會因為連接客戶的數目的增長而不斷增加.我們不能夠不考慮到開銷增加到一定程度的時候系統會承受不住的可能.因此,得想個辦法限制連接客戶的數量,提高伺服器的效率.

那麼解決的方案是:在伺服器端,我們在伺服器啟動時創建一定數量的PooledConnectionHandler,我們把進入的連線放入一個連線池中並讓PooledConnectionHandler 打理剩下的事情.客戶端的程式完全不用修改,這樣設計的優點如下:

1.限定了答應同時連線的數目。
2.只需要啟動PooledConnectionHandler線程有限次

這兩句話的涵義將在其後的程式中得到體現,下面是PooledRemoteFileServer 類別的結構:

import java.io.*;
import java.net. *;
import java.util.*;

public class PooledRemoteFileServer {
PRotected int maxConnections;//定義能同時處理的客戶機連線的最大數目protected int listenPort;//定義要監聽的連接埠號碼;

public PooledRemoteFileServer(int aListenPort, int maxConnections) {
listenPort = aListenPort;
this.maxConnections = maxConnections;
}
public static void main(​​nidal[] maxConnections;
}
public static void main(​​nidal[] publicoids[] />建立數目為maxConnections的PooledConnectionHandler
}
public void acceptConnections(){//在ServerSocket 上監聽傳入的客戶機連線,和前面的RemoteFileServer,MultiThreadRemoteFileServer中的監聽程式完全一樣
) {
}//連接處理程序
}

同樣,首先來看main函數
public static void main(String[] args) {
PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3); //與前面所有伺服器的main函式不同,我們先要建立一個連線池,這個池裡面有三個可用的connectionHandler
server.acceptConnections();//一旦就緒,就開始監聽

下面我們就來看創建三個connectionHandler的程式如何實作:

public void setUpHandlers(){
for (int i = 0; i PooledHandler currentHandler = new PooledConnectionHandler(); i).start();
}
}

setUpHandlers() 方法創建maxConnections個PooledConnectionHandler並在新Thread 中激活它們. PooledConnectionHandler在這裡是一個用接口(Runnable)實現的線程體.用實現了Runnable的對象來建立Thread使我們可以在Thread 呼叫start() 並且可以期望在Runnable上呼叫了run()。也就是說,我們的 PooledConnectionHandler 將等著處理進入的連接,每個都在它自己的Thread中進行。我們在範例中只創建三個Thread,而且一旦伺服器運行,這就不能被改變。

acceptConnections()方法這裡就略去不寫了,看看連接處理程序handleConnection(Socket incomingConnection)

protected void handleConnection(Socket connectionToHandle) {
PooledHandler.processConnectionRequest(connectionToHandle);直接呼叫了PooledConnectionHandler線程類別的類別方法processRequest對監聽到的連接進行處理,顯然這個processRequest是一個靜態方法.

PooledRemoteFileServer類別中的方法均涉及到一個重要的線程類別,PooledConnectionHandler.下面就看看這樣一個這樣一個類別中的方法都涉及到一個重要的線程類別,PooledConnectionHandler.下面就看看這樣一個用介面實現的類別長什麼樣子:

public class PooledConnectionHandler implements Runnable {
protected Socket connection;//代表目前正在處理的Socket
protected static List pool = new LinkedList();//List/名為
protected static List pool = new LinkedList();//List/名稱需被處理的連接,也就是用LinkedList來模擬一個連接池
public PooledConnectionHandler() {//建構子
}
public void handleConnection() {//對連接的I/O操作在這裡了
}
public static void processRequest(Socket requestToHandle) {//處理客戶連接,將他們加入連接池
}
public void run() {//等待有連接來,來了,就調handleConnection()處理
}
}

可以看出,這個類別與多執行緒Socket那一天的ConnectionHandler非常相似,但不同的是,它帶有處理連接池的手段.

首先看看要在監聽程式中用到的processRequest()方法
public static void processRequest(Socket requestToHandle) {
synchronized (pool) {
pool.add(pool.size(), requestToHandle);
pool.notifyAll();
}
}
)
連接socket.可以這樣說,processRequest所做的工作就是把客戶連接加入到連接池當中.但是要確保在對連接池(Pool)進行操作的時候沒有其他的線程幹擾,就需要獲取連接池的對象鎖,並使用同步塊,前面所了解有關synchronized的概念在這裡就可以派上用場了.

那麼,既然已經保證了我們是唯一“涉水”的人,我們就可以把傳入的Socket 添加到LinkedList 的尾端.一旦我們添加了新的連接,我們就可以使用pool.notifyall通知其它正在等待該池的Thread,連接池的對象鎖解除,現在可用了.從另外一個角度來說,也可以說是通知另外一個正在等待的線程,一些條件已經具備了.

那麼這個在等待的線程是什麼呢?

我們來實現PooledConnectionHandler上的run()方法,它將在連接池上等待,並且池中一有連接就處理它,所以是這個要處理連接的線程在等待著呢:

public void run() {
while (true) {
synchronized (pool) {
while (pool.isEmpty()) {
try {
pool.wait();
} catch (InterruptedException e) {return;}
}
connection = (Socket) pool.remove(0);//攫取池中的第一個連接,使之成為馬上就要處理的connection
}
handleConnection();//然後交給handleConnection處理
}
}

這個函數告訴了我們每個PooledConnectionHandler線程主要都在run些什麼.顯然,它是要不停的去看連接池中有沒有接入的連接,假如有,馬上處理,因此它在等待"連接池有連接了"這樣一個條件.那麼誰來告訴它這個條件滿足了呢,顯然是剛才的processRequest.當processRequest發出通知(pool.notify())的時候,這個條件就滿足了,這時run()中的處理程序就不用再繼續等待了,就可以馬上去出一個連接進行處理.反過來說,在這個條件沒有滿足之前,wait()所在的線程還是要處於阻塞狀態,或者是說停滯狀態.由於同樣要對pool進行操作,所以這裡也需要使用到同步塊.

讓我們再次復習一下對象鎖的概念.當run() 擁有池的互斥鎖時,processRequest() 如何能夠把連線放到池中呢?答案是對池上的 wait() 的呼叫釋放鎖,而 wait() 接著就在自己返回之前再次攫取該鎖。這就使得池物件的其它同步程式碼可以取得該鎖。

最後,我們看看PooledConnectionHandler線程中的handleConnection()方法.跟在多線程伺服器中不同,我們的PooledConnectionHandler有一個 handleConnection() 方法。這個方法的程式碼跟非池式的,也就是多執行緒伺服器中的ConnectionHandler上的 run() 方法的程式碼完全一樣。首先,我們把 OutputStream 和 InputStream 分別包裝進(用 Socket 上的 getOutputStream() 和 getInputStream())BufferedReader 和 PrintWriter。然後我們逐行讀目標文件,就像我們在多執行緒範例中所做的那樣。再一次,我們取得一些位元組之後就把它們放到本地的 line 變數中,然後寫出到客戶機。完成讀寫操作之後,我們關閉 FileReader 和開啟的流。

講到這裡,我們可以看到,程式中有兩個類別,PooledRemoteFileServer和PooledConnectionHandler.PooledRemoteFileServer並不直接處理連接請求,它只是負責監聽這些連接,並把他們仍到連接池裡面,至於處理的具體環節,都交給PooledConnectionHandler負責了。

我們的帶有連接池的伺服器研究完了。讓我們回顧一下建立和使用「池版」伺服器的步驟:

1.建立一個新種類的連接處理程序(我們稱之為 PooledConnectionHandler)來處理池中的連接。
2.修改伺服器以建立和使用一組 PooledConnectionHandler。

附:PooledRemoteFileServer.java的原始碼

附:PooledRemoteFileServer.java的源碼

import java.io.*;
import java.net.*;
import java.util.*;

public class PoolConnectionedRemf​​idoo​​d format​​oid表;
protected ServerSocket serverSocket;
public PooledRemoteFileServer(int aListenPort, int maxConnections) {
listenPort = aListenPort;
this.maxConnections = maxConnections;
}Connection 5 );
SocketcomingConnection = null;
while (true) {
incomingConnection = server.accept();
handleConnection(incomingConnection);
}
} catch (BindException e) {
System.out.print
} catch (BindException e) {
System.out.println("無法綁定到連接埠.print" ListenPort);
} catch (IOException e) {
System.out.println("無法在連接埠上實例化ServerSocket: " + ListenPort);
}
}
protected void handleces(Socket connConnection}
}
protected void handleces(Socket connConnection”Handle connectionToHandle);
}
public static void main(String[] args) {
PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3);
server.setUpHandlers(); ) {
for (int i = 0; i PooledConnectionHandler currentHandler = new PooledConnectionHandler();
new Thread(currentHandler, "Handler " + i).start();
^
class PooledConnectionHandler 實作Runnable {
protected Socket 連線;
protected static List pool = new LinkedList();
public PooledHandler() {
}
public v.handleConnection(c) handleConnection() 電話
}
) handleConnection(c]le)。 getOutputStream());
BufferedReader streamReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

String fileToRead = streamReader.readLine(); String line = null;
while ((line = fileReader.readLine()) != null)
streamWriter.println(line);

fileReader.close();
streamWriter.close();
fileReader.close();
streamWriter.. ;
} catch (FileNotFoundException e) {
System.out.println("在伺服器上找不到要求的檔案。");
} catch (IOException e) {
System.out.println("處理客戶端時錯誤: " + e);
}
}
public static void processRequest(Socket requestToHandle) {
synchronized (pool) {
pool.add(pool.size(), requestToHandle);
}Allify();
}
public void run() {
while (true) {
synchronized (pool) {
while (pool.isEmpty()) {
try {
pool.wait(); return ;
}
}
connection = (Socket) pool.remove(0);
}
handleConnection();
}
}


 內容請關注PHP中文網(www.php.cn)!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn