<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熱點博客

    好友博客

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲乱码一二三四五六区| 欧洲美女大片免费播放器视频| 精品免费国产一区二区| 国产精品成人啪精品视频免费| 久久久久久久亚洲Av无码| 国产精品极品美女免费观看| 女人隐私秘视频黄www免费| 亚洲中文字幕一二三四区| 亚洲人成网站在线观看播放| 成年女人18级毛片毛片免费| a级毛片在线免费| 亚洲高清乱码午夜电影网| 亚洲成A∨人片在线观看不卡| 麻豆国产入口在线观看免费| 国产精品白浆在线观看免费| jizzjizz亚洲日本少妇| 亚洲综合无码一区二区三区| 亚洲一区日韩高清中文字幕亚洲 | 18禁免费无码无遮挡不卡网站 | 亚洲一卡二卡三卡| 在线亚洲精品自拍| 日韩电影免费在线观看视频| 免费女人高潮流视频在线观看| 黄色三级三级三级免费看| 激情五月亚洲色图| 亚洲福利在线视频| 日韩一卡2卡3卡4卡新区亚洲| 午夜视频在线在免费| av免费不卡国产观看| 国产成人一区二区三区视频免费| 国产亚洲精品美女| 亚洲精品中文字幕无码A片老| 亚洲综合亚洲国产尤物| 国产精品亚洲一区二区三区在线| www国产亚洲精品久久久日本| 一个人免费观看在线视频www | 亚洲中文字幕久久精品无码APP| 韩国日本好看电影免费看| 亚色九九九全国免费视频| 一级毛片免费观看| 黄色网址在线免费|