NIO執行緒模型

什麼是NIO執行緒模型?

上圖是NIO的執行緒模型,  基於select實現,   這種執行緒模型的特點:  多條channel通過一個選擇器和單挑執行緒繫結, 並且在這種程式設計模型中, Channel中相關業務邏輯不允許存在耗時的任務 , 如果一定會有耗時的邏輯, 請將它們放置到執行緒池中去執行,  因為這種模型雖然做到了非阻塞, 但是他並不是真正的非同步程式設計, 任何channel上的任何耗時的操作, 都會拖垮這個選擇器, 進而拖垮整條執行緒 , 這也是為啥它會被稱為 同步非阻塞

 什麼是同步?

  • 其一: 因為當channel中出現了耗時的操作時, 其他的channel不得不同步等待
  • 其二: 從編碼上看: NIO程式設計中 服務端的select() 會同步等待選擇器感興趣的事件發生
  • 其三: 從作業系統的角度上看, 程式使用的資料來自 網絡卡 -> 作業系統的核心緩衝區 -> 使用者區, 當資料進入使用者區後java程式便可以對其進行讀寫操作, 所謂同步就是: 資料進入使用者區的過程中,NIO程式設計模型需要同步並不停的詢問

NIO執行緒模型的優點

NIO執行緒執行緒模型相對於傳統的BIO來說, 最大的優勢就是在於 NIO執行緒模型中單條執行緒可同時為N個使用者(Channel)服務, 而BIO程式設計模型讓人詬病的地方就是, 任何一個新連線接入, 伺服器都得為他開啟不止一條新的執行緒去執行它, 這種BIO系統中, 併發肯定不會很高

NIO適用場景:

NIO方式適用於連線數目多且連線比較短(輕操作)的架構,比如聊天伺服器,併發侷限於應用中,程式設計比較複雜,JDK1.4開始支援

AIO (Asynchronous Input/Output )模型

什麼是AIO?

AIO是(jdk1.7) 發行的 非同步IO程式設計模型, 真正實現了非同步IO, 基於Linux系統的 Epoll 機制實現

無論是NIO, 還是AIO底層都沒有改變網路通訊的基本步驟, 而是在這個基礎上進行了一系列的升級

AIO的底層實現是由作業系統完成的, 資料在核心空間&使用者空間的遷移, 我們在編寫程式碼時也是這樣, 只需要呼叫 AIO.read() 或者是 AIO.write() 即可, 換句話說, 我們的業務邏輯就成了 回撥, 原來在作業系統處理資料的這個過程中, 我們的程式需要阻塞等待著, 亦或者放線上程池中執行, 而在AIO程式設計中這段等待時間差被省去了, 因為當作業系統認為資料還有沒準備完時, 它是不會打擾我們的程式的, 這時我們的程式可以去處理其他的邏輯, 而一旦作業系統認為資料齊全了, 他就會回撥我們的提供的回撥函式

  • 對應作業系統來說, 當有流資料可讀時, 作業系統會將流傳入到read方法的緩衝區, 然後回撥相關的 CompletionHandler
  • 對於寫操作而言, 作業系統會將程式中Buffer裡面資料寫入到從使用者空間寫入到系統空間 再寫入到網絡卡中, 寫入完畢, 同樣會回撥相關的回撥函式

AIO程式設計Server端的示例

下面貼出來一個AIO程式設計Server端的例項:

像下面的 read() write() accept() 全是非同步的, 一經呼叫即刻返回, 不一樣的地方是我們會提供一個回撥物件, 留給作業系統, 當作業系統認為讀寫資料都到位了, 就會去回撥這些函式

public class AIOServer {
    private ExecutorService executorService;

    // 服務端的Channel
    private AsynchronousServerSocketChannel asynchronousServerSocketChannel;

    private AIOServer(int port) {
        init(port);
    }

    // 初始化
    private void init(int port) {
        System.out.println("aio server start with port " + port);

        executorService = Executors.newFixedThreadPool(5);

        try {
            // 開啟服務端的通道
            asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
            // 繫結埠
            asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
            System.out.println("server start ... ");

            /**
             *  方法會非同步的去接收一個請求, accept()同樣是
             *  引數1 : this , 暫時理解成任意型別的
             *  引數2 : CompleteHandler -- 當請求到來後,會交付給 AIOServerHandler進行處理
             *
             * todo 在 AIO中的監聽並不是while(true), 而是類似遞迴的操作, 每次監聽到客戶端的請求後, 都需要在處理邏輯中開啟下一次的監聽
             */
            asynchronousServerSocketChannel.accept(this, new AIOServerHandler());
            System.out.println("------------------------------");
            // 阻塞程式
            try {
                TimeUnit.SECONDS.sleep(60);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public AsynchronousServerSocketChannel getAsynchronousServerSocketChannel() {
       return this.asynchronousServerSocketChannel;
    }

    public static void main(String[] args) {
        AIOServer aioServer = new AIOServer(9999);
    }
}

AIO的適用場景

AIO方式使用於連線數目多且連線比較長(重操作)的架構,比如相簿伺服器,充分呼叫OS參與併發操作,程式設計比較複雜,JDK7開始支援。

我是bloger 賜我白日夢, 歡迎關注我 --武漢加油