<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    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 閱讀(7264) 評論(0)  編輯  收藏 所屬分類: J2SE
    <2008年8月>
    272829303112
    3456789
    10111213141516
    17181920212223
    24252627282930
    31123456

    常用鏈接

    留言簿(21)

    隨筆分類

    隨筆檔案

    BlogJava熱點博客

    好友博客

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲高清免费在线观看| 亚洲人成77777在线播放网站不卡 亚洲人成77777在线观看网 | 成人无码区免费视频观看| 精品亚洲成a人片在线观看少妇| 国产在线播放线91免费| 亚洲乱码日产精品a级毛片久久| 美景之屋4在线未删减免费| 国产又大又黑又粗免费视频 | 午夜亚洲乱码伦小说区69堂| 国产又大又长又粗又硬的免费视频 | 最近免费中文字幕大全视频| 亚洲va久久久久| 在线观看免费a∨网站| 日韩国产欧美亚洲v片| 亚洲第一黄色网址| 中国一级特黄高清免费的大片中国一级黄色片 | 337P日本欧洲亚洲大胆艺术图| 国产精品成人免费综合| 最新亚洲人成无码网www电影| 免费萌白酱国产一区二区| 日韩少妇内射免费播放| 人人狠狠综合久久亚洲婷婷| 少妇人妻偷人精品免费视频| 亚洲国产成人精品久久| 日本免费一本天堂在线| 黄 色一级 成 人网站免费| 亚洲AV无码乱码在线观看裸奔| 足恋玩丝袜脚视频免费网站| 亚洲成年网站在线观看| 亚洲国产主播精品极品网红| 国产免费拔擦拔擦8X高清在线人| 亚洲色图古典武侠| 国产美女做a免费视频软件| 国产无限免费观看黄网站| 91亚洲国产在人线播放午夜| 日韩欧美一区二区三区免费观看| 亚洲国产日韩a在线播放| 在线亚洲精品自拍| 最近2019中文字幕免费大全5| 亚洲成A人片在线播放器| 亚洲综合熟女久久久30p|