Java IO
在Client/Server模型中,Server往往需要同時處理大量來自Client的訪問請求,因此Server端需采用支持高並發訪問的架構。一種簡單而又直接的解決方案是“one-thread-per-connection”。這是一種基於阻塞式I/O的多線程模型。在該模型中,Server為每個Client連接創建一個處理線程,每個處理線程阻塞式等待可能達到的數據,一旦數據到達,則立即處理請求、返回處理結果並再次進入等待狀態。由於每個Client連接有一個單獨的處理線程為其服務,因此可保證良好的響應時間。但當系統負載增大(並發請求增多)時,Server端需要的線程數會增加,這將成為系統擴展的瓶頸所在。(Java IO與NIO的區別)
Java NIO
Java NIO不但引入了全新的高效的I/O機制,同時引入了基於Reactor設計模式的多路復用異步模式。NIO包中主要包含以下幾種抽象數據類型。
* Channel(通道):NIO把它支持的I/O對象抽象為Channel。它模擬了通信連接,類似於原I/O中的流(Stream),用戶可以通過它讀取和寫入數據。目前已知的實例類有SocketChannel、ServerSocketChannel、DatagramChannel、FileChannel等。
* Buffer(緩沖區):Buffer是一塊連續的內存區域,一般作為Channel收發數據的載體出現。所有數據都通過Buffer對象來處理。
* Selector(選擇器):Selector類提供了監控一個和多個通道當前狀態的機制。只要Channel向Selector注冊了某種特定的事件,Selector就會監聽這些事件是否會發生,一旦發生某個事件,便會通知對應的Channel。使用選擇器,借助單一線程,就可對數量龐大的活動I/O通道實施監控和維護。
Java NIO的服務端只需啟動一個專門的線程來處理所有的IO事件,這種通信模型是怎么實現的呢?Java NIO采用了雙向通道(channel)進行數據傳輸,而不是單向的流(stream),在通道上可以注冊我們感興趣的事件。一共有以下四種事件:
事件名 對應值
服務端接收客戶端連接事件 SelectionKey.OP_ACCEPT(16)
客戶端連接服務端事件 SelectionKey.OP_CONNECT(8)
讀事件 SelectionKey.OP_READ(1)
寫事件 SelectionKey.OP_WRITE(4)
服務端和客戶端各自維護一個管理通道的對象,我們稱之為selector,該對象能檢測一個或多個通道 (channel) 上的事件。我們以服務端為例,如果服務端的selector上注冊了讀事件,某時刻客戶端給服務端發送了一些數據,阻塞I/O這時會調用read()方法阻塞地讀取數據,而NIO的服務端會在selector中添加一個讀事件。服務端的處理線程會輪詢地訪問selector,如果訪問selector時發現有感興趣的事件到達,則處理這些事件,如果沒有感興趣的事件到達,則處理線程會一直阻塞直到感興趣的事件到達為止。下面是Java NIO的通信模型示意圖:
從下圖中可以看出,當有讀或寫等任何注冊的事件發生時,可以從Selector中獲得相應的SelectionKey,同時從 SelectionKey中可以找到發生的事件和該事件所發生的具體的SelectableChannel,以獲得客戶端發送過來的數據。 使用NIO中非阻塞I/O編寫服務器處理程序,大體上可以分為下面三個步驟:
1. 向Selector對象注冊感興趣的事件
2. 從Selector中獲取感興趣的事件
3. 根據不同的事件進行相應的處理