JAVA NIO全稱Java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,為所有的原始類型(boolean類型除外)提供緩存支持的數據容器,使用它可以提供非阻塞式的高伸縮性網絡。
Sun 官方標榜的特性如下: 為所有的原始類型提供(Buffer)緩存支持。字符集編碼解碼解決方案。
Channel :一個新的原始I/O 抽象。支持鎖和內存映射文件的文件訪問接口。 提供多路(non-blocking) 非阻塞式的高伸縮性網絡I/O 。

NIO 與原來的 I/O 有同樣的作用和目的,但是它使用不同的方式? 塊I/O。塊 I/O 的效率可以比流 I/O 高許多。
流與塊的比較
原來的 I/O 庫(在 java.io.*中) 與 NIO 最重要的區別是數據打包和傳輸的方式。正如前面提到的,原來的 I/O 以流的方式處理數據,而 NIO 以塊的方式處理數據。
面向流 的 I/O 系統一次一個字節地處理數據。一個輸入流產生一個字節的數據,一個輸出流消費一個字節的數據。為流式數據創建過濾器非常容易。鏈接幾個過濾器,以便每個過濾器只負責單個復雜處理機制的一部分,這樣也是相對簡單的。不利的一面是,面向流的 I/O 通常相當慢。
一個 面向塊 的 I/O 系統以塊的形式處理數據。每一個操作都在一步中產生或者消費一個數據塊。按塊處理數據比按(流式的)字節處理數據要快得多。但是面向塊的 I/O 缺少一些面向流的 I/O 所具有的優雅性和簡單性。
在NIO中有幾個核心對象需要掌握:緩沖區(Buffer)、通道(Channel)、選擇器(Selector)。
通道Channel
Channel是一個對象,可以通過它讀取和寫入數據。拿 NIO 與原來的 I/O 做個比較,通道就像是流,而且他們面向緩沖區的。
正如前面提到的,所有數據都通過 Buffer 對象來處理。我們永遠不會將字節直接寫入通道中,相反,是將數據寫入包含一個或者多個字節的緩沖區。同樣,將不會直接從通道中讀取字節,而是將數據從通道讀入緩沖區,再從緩沖區獲取這個字節。
通道與流的不同之處在于通道是雙向的。而流只是在一個方向上移動(一個流必須是 InputStream 或者 OutputStream 的子類), 而 通道 可以用于讀、寫或者同時用于讀寫。
因為它們是雙向的,所以通道可以比流更好地反映底層操作系統的真實情況。特別是在 UNIX 模型中,底層操作系統通道是雙向的。
Channel中最常用的三個類方法就是map、read和write,其中map方法用于將Channel對應的部分或全部數據映射成ByteBuffer,而read或write方法有一系列的重載形式,這些方法用于從Buffer中讀取數據或向Buffer中寫入數據。
1、使用NIO讀取數據
在前面我們說過,任何時候讀取數據,都不是直接從通道讀取,而是從通道讀取到緩沖區。所以使用NIO讀取數據可以分為下面三個步驟:
- (1). 從FileInputStream獲取Channel
- (2). 創建Buffer
- (3). 將數據從Channel讀取到Buffer中
2、使用NIO寫入數據
使用NIO寫入數據與讀取數據的過程類似,同樣數據不是直接寫入通道,而是寫入緩沖區,可以分為下面三個步驟:
- (1). 從FileInputStream獲取Channel
- (2). 創建Buffer
- (3). 將數據從Channel寫入到Buffer中
緩沖區
是一個固定數據量的指定基本類型的數據容器。除內容之外,緩沖區還具有位置 和界限,其中位置是要讀寫的下一個元素的索引,界限是第一個應該讀寫的元素的索引。基本 Buffer 類定義了這些屬性以及清除、反轉 和重繞 方法,用以標記 當前位置,以及將當前位置重置 為前一個標記處。
每個非布爾基本類型都有一個緩沖區類。每個類定義了一系列用于將數據移出或移入緩沖區的 get 和 put 方法,用于壓縮、復制 和切片 緩沖區的方法,以及用于分的異類或同類二進制數據序列),訪問要么是以 big-endian字節順序進行,要么是以 little-endian 字節順序進行。
在NIO中,所有的緩沖區類型都繼承于抽象類Buffer,最常用的就是ByteBuffer,對于Java中的基本類型,基本都有一個具體Buffer類型與之相對應,它們之間的繼承關系如下圖所示:

選擇器Selector
Selector類是NIO的核心類,Selector能夠檢測多個注冊的通道上是否有事件發生,如果有事件發生,便獲取事件然后針對每個事件進行相應的響應處理。這樣一來,只是用一個單線程就可以管理多個通道,也就是管理多個連接。這樣使得只有在連接真正有讀寫事件發生時,才會調用函數來進行讀寫,就大大地減少了系統開銷,并且不必為每個連接都創建一個線程,不用去維護多個線程,并且避免了多線程之間的上下文切換導致的開銷。
與Selector有關的一個關鍵類是SelectionKey,一個SelectionKey表示一個到達的事件,這2個類構成了服務端處理業務的關鍵邏輯。