2.3.1 NIO類庫簡介
新的輸入/輸出(NIO)庫是在JDK 1.4中引入的。NIO彌補了原來同步阻塞I/O的不足,它在標準Java代碼中提供了高速的、面向塊的I/O。通過定義包含數據的類,以及通過以塊的形式處理這些數據,NIO不用使用本機代碼就可以利用低級優化,這是原來的I/O包所無法做到的。下面我們對NIO的一些概念和功能做下簡單介紹,以便大家能夠快速地了解NIO類庫和相關概念。
1.緩沖區Buffer
我們首先介紹緩沖區(Buffer)的概念,Buffer是一個對象,它包含一些要寫入或者要讀出的數據。在NIO類庫中加入Buffer對象,體現了新庫與原I/O的一個重要區別。在面向流的I/O中,可以將數據直接寫入或者將數據直接讀到Stream對象中。
在NIO庫中,所有數據都是用緩沖區處理的。在讀取數據時,它是直接讀到緩沖區中的;在寫入數據時,寫入到緩沖區中。任何時候訪問NIO中的數據,都是通過緩沖區進行操作。
緩沖區實質上是一個數組。通常它是一個字節數組(ByteBuffer),也可以使用其他種類的數組。但是一個緩沖區不僅僅是一個數組,緩沖區提供了對數據的結構化訪問以及維護讀寫位置(limit)等信息。
最常用的緩沖區是ByteBuffer,一個ByteBuffer提供了一組功能用于操作byte數組。除了ByteBuffer,還有其他的一些緩沖區,事實上,每一種Java基本類型(除了Boolean類型)都對應有一種緩沖區,具體如下。
◎ ByteBuffer:字節緩沖區
◎ CharBuffer:字符緩沖區
◎ ShortBuffer:短整型緩沖區
◎ IntBuffer:整形緩沖區
◎ LongBuffer:長整形緩沖區
◎ FloatBuffer:浮點型緩沖區
◎ DoubleBuffer:雙精度浮點型緩沖區
緩沖區的類圖繼承關系如圖2-8所示。

圖2-8 Buffer繼承關系圖
每一個Buffer類都是Buffer接口的一個子實例。除了ByteBuffer,每一個 Buffer類都有完全一樣的操作,只是它們所處理的數據類型不一樣。因為大多數標準I/O操作都使用ByteBuffer,所以它除了具有一般緩沖區的操作之外還提供一些特有的操作,方便網絡讀寫。
2.通道Channel
Channel是一個通道,可以通過它讀取和寫入數據,它就像自來水管一樣,網絡數據通過Channel讀取和寫入。通道與流的不同之處在于通道是雙向的,流只是在一個方向上移動(一個流必須是InputStream或者OutputStream的子類),而且通道可以用于讀、寫或者同時用于讀寫。
因為Channel是全雙工的,所以它可以比流更好地映射底層操作系統的API。特別是在UNIX網絡編程模型中,底層操作系統的通道都是全雙工的,同時支持讀寫操作。
Channel的類圖繼承關系如圖2-9所示。
自頂向下看,前三層主要是Channel接口,用于定義它的功能,后面是一些具體的功能類(抽象類),從類圖可以看出,實際上Channel可以分為兩大類:分別是用于網絡讀寫的SelectableChannel和用于文件操作的FileChannel。
本書涉及的ServerSocketChannel和SocketChannel都是SelectableChannel的子類,關于它們的具體用法將在后續的代碼中體現。

圖2-9 Channel繼承關系類圖
3.多路復用器Selector
在本節中,我們將探索多路復用器Selector,它是Java NIO編程的基礎,熟練地掌握Selector對于掌握NIO編程至關重要。多路復用器提供選擇已經就緒的任務的能力。簡單來講,Selector會不斷地輪詢注冊在其上的Channel,如果某個Channel上面有新的TCP連接接入、讀和寫事件,這個Channel就處于就緒狀態,會被Selector輪詢出來,然后通過SelectionKey可以獲取就緒Channel的集合,進行后續的I/O操作。
一個多路復用器Selector可以同時輪詢多個Channel,由于JDK使用了epoll()代替傳統的select實現,所以它并沒有最大連接句柄1024/2048的限制。這也就意味著只需要一個線程負責Selector的輪詢,就可以接入成千上萬的客戶端,這確實是個非常巨大的進步。
下面,我們通過NIO編程的序列圖和源碼分析來熟悉相關的概念,以便鞏固我們前面所學的NIO基礎知識。