Java 中 3 種常見 IO 模型
BIO (Blocking I/O)
BIO 屬於同步阻塞 IO 模型 。
同步阻塞 IO 模型中,應用程序發起 read 調用后,會一直阻塞,直到在內核把數據拷貝到用戶空間。

在客戶端連接數量不高的情況下,是沒問題的。但是,當面對十萬甚至百萬級連接的時候,傳統的 BIO 模型是無能為力的。因此,我們需要一種更高效的 I/O 處理模型來應對更高的並發量。
NIO (Non-blocking/New I/O)
Java 中的 NIO 於 Java 1.4 中引入,對應 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解為 Non-blocking,不單純是 New。它支持面向緩沖的,基於通道的 I/O 操作方法。對於高負載、高並發的(網絡)應用,應使用 NIO 。
Java 中的 NIO 可以看作是 I/O 多路復用模型。也有很多人認為,Java 中的 NIO 屬於同步非阻塞 IO 模型。
跟着我的思路往下看看,相信你會得到答案!
我們先來看看 同步非阻塞 IO 模型。

同步非阻塞 IO 模型中,應用程序會一直發起 read 調用,等待數據從內核空間拷貝到用戶空間的這段時間里,線程依然是阻塞的,直到在內核把數據拷貝到用戶空間。
相比於同步阻塞 IO 模型,同步非阻塞 IO 模型確實有了很大改進。通過輪詢操作,避免了一直阻塞。
但是,這種 IO 模型同樣存在問題:應用程序不斷進行 I/O 系統調用輪詢數據是否已經准備好的過程是十分消耗 CPU 資源的。
這個時候,I/O 多路復用模型 就上場了。

IO 多路復用模型中,線程首先發起 select 調用,詢問內核數據是否准備就緒,等內核把數據准備好了,用戶線程再發起 read 調用。read 調用的過程(數據從內核空間->用戶空間)還是阻塞的。
目前支持 IO 多路復用的系統調用,有 select,epoll 等等。select 系統調用,是目前幾乎在所有的操作系統上都有支持
- select 調用 :內核提供的系統調用,它支持一次查詢多個系統調用的可用狀態。幾乎所有的操作系統都支持。
- epoll 調用 :linux 2.6 內核,屬於 select 調用的增強版本,優化了 IO 的執行效率。
IO 多路復用模型,通過減少無效的系統調用,減少了對 CPU 資源的消耗。
Java 中的 NIO ,有一個非常重要的選擇器 ( Selector ) 的概念,也可以被稱為多路復用器。通過它,只需要一個線程便可以管理多個客戶端連接。當客戶端數據到了之后,才會為其服務。

AIO (Asynchronous I/O)
AIO 也就是 NIO 2。Java 7 中引入了 NIO 的改進版 NIO 2,它是異步 IO 模型。
異步 IO 是基於事件和回調機制實現的,也就是應用操作之后會直接返回,不會堵塞在那里,當后台處理完成,操作系統會通知相應的線程進行后續的操作。

目前來說 AIO 的應用還不是很廣泛。Netty 之前也嘗試使用過 AIO,不過又放棄了。這是因為,Netty 使用了 AIO 之后,在 Linux 系統上的性能並沒有多少提升。
最后,來一張圖,簡單總結一下 Java 中的 BIO、NIO、AIO。

BIO、NIO、AIO:
- Java BIO : 同步並阻塞,服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。
- Java NIO : 同步非阻塞,服務器實現模式為一個請求一個線程,即客戶端發送的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有I/O請求時才啟動一個線程進行處理。
- Java AIO(NIO.2) : 異步非阻塞,服務器實現模式為一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啟動線程進行處理。
BIO、NIO、AIO適用場景分析:
- BIO方式適用於連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,並發局限於應用中,JDK1.4以前的唯一選擇,但程序直觀簡單易理解。
- NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,並發局限於應用中,編程比較復雜,JDK1.4開始支持。
- AIO方式使用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與並發操作,編程比較復雜,JDK7開始支持。Netty!
參考:公眾號JavaGuide ,作者Guide哥
