posts - 262,  comments - 221,  trackbacks - 0
          在網上無意看到一個多線程的Socket服務器端例子,覺得非常不錯。特別是其中的線程池的思想,簡單而且高效。雖然JDK1.5開始已經自帶了線程池包,但該代碼不失為學習Socket和多線程的一個好的入門例子。

          下面的代碼是對該例子的簡單整理,補充了注釋。

          【代碼一】PooledConnectionHandler:后臺處理類
          package server;

          import java.io.BufferedReader;
          import java.io.FileNotFoundException;
          import java.io.FileReader;
          import java.io.IOException;
          import java.io.InputStreamReader;
          import java.io.PrintWriter;
          import java.net.Socket;
          import java.util.LinkedList;
          import java.util.List;

          /**
           * <pre>
           * PooledConnectionHandler實現了Runnable接口,它的用途是處理服務器端傳來的Socket連
           * 接。該類維護了一個被稱為"連接池"的全局鏈式列表(靜態),該列表在類被加載時創建。
           * 
           * 當該類的run()方法被調用時,它首先檢查該"連接池"中是否有需要待處理的客戶端連接,如果
           * 沒有(可能是請求尚未到來)則調用wait()方法等待,而另一個靜態方法processRequest則負
           * 責接收客戶端請求并添加到"連接池"的末尾,然后通知所有正在等待的線程,各個等待的線程則
           * 以"互斥"的方式競爭資源(請求)當某個線程率先獲取到對象鎖,拿到一個連接后,將釋放鎖,然
           * 后在自己的線程中處理請求。各個線程之間彼此不會互相影響。
           * 
           * 需要注意的是這個類的一個方法:processRequest這個方法第一個為靜態方法,因為對于這個
           * 方法來說,它只是一個負責通知的角色,所以沒有必要是對象級的。將其修飾符置為static是合
           * 理的。
           * 
           * 其次我們看到在對全局資源"連接池"進行操作時,不管是往池中添加請求,還是從池中取出請求,
           * 都需要在關鍵的語句塊中增加synchronized{},來保證同一時刻不會有多個線程競爭同一個
           * 資源,或者在添加資源未完成前有另一個線程試圖讀取該資源。
           * 
           * 最后一個要注意的地方是其中的wait()和notifyAll()方法,wait()方法的調用發送在線程創建
           * 完成,但請求尚未到來之前,這時線程并不持有對List的鎖,而notifyAll()方法喚起所有等待
           * 的線程,所有等待線程將一起競爭鎖,此時只有一個線程可能檢測到池非空而進入池中請求資
           * 源。
           * </pre>
           
          */
          public class PooledConnectionHandler implements Runnable {

              
          /** The client connection of Socket. */
              
          protected Socket connection;

              
          /** The request pool. */
              
          protected static List pool = new LinkedList();

              
          /**
               * Instantiates a new pooled connection handler.
               
          */
              
          public PooledConnectionHandler() {
              }
              
              
          /*
               * (non-Javadoc)
               * 
               * @see java.lang.Runnable#run()
               
          */
              
          public void run() {
                  
          while (true) {
                      
          // 因為可能有多個線程同時去Pool中取Socket進行處理。
                      
          // 所以這里我們需同步,防止同一個請求被多次處理
                      synchronized (pool) {
                          
          while (pool.isEmpty()) {
                              
          try {
                                  pool.wait();
          // 沒有請求到來則等待
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                          }
                          
          // 從池中取出一個Socket,準備進行處理
                          connection = (Socket) pool.remove(0);
                      }
                      
          // 取到Socket后則不需要同步了,因為此時是Connection是對象
                      
          // 級屬性,在線程內部自己處理,不涉及公共資源的訪問
                      handleConnection();
                  }
              }
              
              
          /**
               * Process request, append Socket to pool and notify all waitting thread
               * 
               * 
          @param requestToHandle the request to handle
               
          */
              
          public static void processRequest(Socket requestToHandle) {
                  
          // 因為有可能在向池中塞請求的時候,另外一個線程
                  
          // 正在從池中取Socket,所以這里需要同步一下
                  synchronized (pool) {
                      
          // 將來自客戶端的請求添加到請求隊列末尾
                      pool.add(pool.size(), requestToHandle);
                      
          // 通知其它正在等待的線程有新請求來到,
                      
          // 此時所有處于wait狀態的線程將被喚醒
                      pool.notifyAll();
                  }
              }    

              
          /**
               * Handle connection.
               
          */
              
          public void handleConnection() {
                  
          try {
                      PrintWriter streamWriter 
          = new PrintWriter(connection
                              .getOutputStream());
                      BufferedReader streamReader 
          = new BufferedReader(
                              
          new InputStreamReader(connection.getInputStream()));

                      String fileToRead 
          = streamReader.readLine();
                      BufferedReader fileReader 
          = new BufferedReader(new FileReader(
                              fileToRead));

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

                      fileReader.close();
                      streamWriter.close();
                      streamReader.close();
                  } 
          catch (FileNotFoundException e) {
                      System.out.println(
          "");
                  } 
          catch (IOException e) {
                      System.out.println(
          "" + e);
                  }
              }    
          }

          【代碼二】PooledRemoteFileServer:多線程服務器端,負責創建線程池并等待客戶端的連接請求
          package server;

          import java.io.IOException;
          import java.net.BindException;
          import java.net.ServerSocket;
          import java.net.Socket;

          /**
           * <pre>
           * PooledRemoteFileServer是一個多線程、池化的Socket服務器。它能夠在指定的端口
           * 監聽來自客戶端的連接請求,同時它限定了允許同時連接的數目。
           * 
           * 在服務器端,服務器啟動時創建指定定數量的后臺處理類實例,這些實例實際上是實現了
           * Runnable接口的類,它們在創建完成后將立刻喚起,不停地監控來自客戶端的連接。
           * 
           * 另一方面,服務器在創建線程之后,將在指定的端口監聽。一旦有客戶端連接上,立刻將
           * 該連接交給后臺在等待的線程去處理,接著立刻返回繼續在端口監聽。這樣的話后臺線程
           * 的處理將不會造成前端服務器監聽的阻塞。
           * </pre>
           
          */
          public class PooledRemoteFileServer {

              
          /** The max connections. */
              
          protected int maxConnections;

              
          /** The listen port. */
              
          protected int listenPort;

              
          /** The server socket. */
              
          protected ServerSocket serverSocket;

              
          /**
               * Instantiates a new pooled remote file server.
               * 
               * 
          @param aListenPort the a listen port
               * 
          @param maxConnections the max connections
               
          */
              
          public PooledRemoteFileServer(int aListenPort, int maxConnections) {
                  listenPort 
          = aListenPort;// 監聽端口
                  this.maxConnections = maxConnections;// 最大同時連接
              }

              
          /**
               * 初始化池:每次創建一個Runnable實例,然后創建線程對象
               
          */
              
          public void setUpHandlers() {
                  
          for (int i = 0; i < maxConnections; i++) {
                      PooledConnectionHandler currentHandler 
          = new PooledConnectionHandler();
                      
          // 線程啟動后將一直監控Socket隊列,以輪詢的方式
                      
          // 監控是否有新的客戶端請求到來,如果有的話則取
                      
          // 出處理,無的話則繼續等待直至請求到來
                      new Thread(currentHandler, "Handler" + i).start();
                  }
              }

              
          /**
               * 接收客戶端連接
               
          */
              
          public void acceptConnections() {
                  
          try {
                      ServerSocket server 
          = new ServerSocket(listenPort, 5);
                      Socket incomingConnection 
          = null;
                      
          while (true) {
                          incomingConnection 
          = server.accept();
                          handleConnection(incomingConnection);
                      }
                  } 
          catch (BindException be) {
                      System.out.println(
          "");
                  } 
          catch (IOException ioe) {
                      System.out.println(
          "" + listenPort);
                  }
              }

              
          /**
               * 在池中處理Socket請求
               * 
               * 
          @param connectionToHandle the connection to handle
               
          */
              
          protected void handleConnection(Socket connectionToHandle) {
                  PooledConnectionHandler.processRequest(connectionToHandle);
              }

              
          public static void main(String args[]) {
                  PooledRemoteFileServer server 
          = new PooledRemoteFileServer(10013);
                  
          // 初始化線程池
                  server.setUpHandlers();
                  
          // 開始在指定端口等待到來的請求
                  server.acceptConnections();
              }
          }

          這個例子的精髓是在PooledConnectionHandler類,它首先創建一個公共的全局“線程池”(LinkList),然后啟動線程監控線程池,與此同時服務器端在接收到客戶端請求后將請求加到“線程池”中,這兩個動作是異步的,在加的時候不允許讀,在讀得到時候不允許加(通過synchronized關鍵字控制),而且多個線程之間并不會互相影響,因為其中的connection屬性是對象級的。

          從這個例子中我們也可以學到在多線程的情況下,哪些變量是必須設置為全局的(static),哪些是必須設置為對象級的:即會被多個線程訪問的資源必須設置為全局的,而跟線程處理狀態,結果有關的屬性一般必須設置為對象級的,以防止互相干擾。

          其次就是在多線程情況下,哪些方法是可以設置為static的而不會出現線程安全的問題,哪些方法是不能設置為靜態方法的:如果方法是屬于控制流程,通知,派發的,那么一般可以設置為靜態的。因為這些方法一般不需要多個,一個就夠了。就如同控制器只要一個就夠了。而業務邏輯實現方法一般不能設置為靜態的,因為靜態方法不能引用對象變量(非靜態變量),但業務邏輯通常是需要針對不同的用戶做出不同的處理的,所以幾乎可以肯定的說是絕對會出現對象變量的。


          -------------------------------------------------------------
          生活就像打牌,不是要抓一手好牌,而是要盡力打好一手爛牌。
          posted on 2008-08-16 23:08 Paul Lin 閱讀(7267) 評論(0)  編輯  收藏 所屬分類: J2SE
          <2008年8月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          常用鏈接

          留言簿(21)

          隨筆分類

          隨筆檔案

          BlogJava熱點博客

          好友博客

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 德格县| 金乡县| 中超| 枣庄市| 深泽县| 平昌县| 双鸭山市| 汶川县| 岑巩县| 温宿县| 吉林市| 敦化市| 张家港市| 博爱县| 宁国市| 香河县| 巴林右旗| 巴楚县| 巴中市| 盘锦市| 罗城| 南阳市| 深泽县| 茶陵县| 上虞市| 泾源县| 永吉县| 炉霍县| 长乐市| 咸宁市| 射阳县| 凤庆县| 武清区| 克什克腾旗| 平塘县| 石嘴山市| 道真| 同德县| 双桥区| 堆龙德庆县| 布尔津县|