標籤:bio nio aio
1. 阻塞與非阻塞
"阻塞"與“非阻塞”概念經常和“同步”、“非同步”混淆。在Java程式中,很多線程通常處於阻塞(blocking)狀態,而同步(並不是指多線程同步的Synchronized)並不是這樣,同步通常是指步驟需要一步步來完成,就想常規的代碼一條條地執行一樣,但非同步可以在沒有執行完當前這行代碼之前,就執行下一行代碼,就像很多JS代碼、UI控制項、後台啟動線程等。
相對於阻塞來講,同步的程式線程應當是處於Running狀態的。線程處於Blocking狀態就差不多可以看成是睡眠的,也就是說它什麼也沒法做,只有一直等待一個訊號的產生才能喚醒。處於Running狀態下的線程是活躍的,在這種情況下可以去做很多其他的事情,可以一直去檢測一些事情是否做好。
2. BIO (Blocking I/O)
Blocking I/O(阻塞I/O),這是最古老的Java通訊機制,在這種模型中,應用程式首先會通過System Call發送請求給核心(Kernel),然後由核心去進行網路通訊,應用程式如果發起的系統調用是讀操作,在核心準備好資料以前,這個線程將會被掛起,一直等待下去,直到有返回的資料在核心中準備好位置,或者在設定SoTimeout後逾時被喚醒。
假如拋開逾時機制在網路等待機制中的作用,發起read()操作等待遠程返回資料的過程由以下兩個階段組成。
階段1:等待I/O返回資料,這取決於I/O請求的目標返回資料的速度。例如在網路I/O請求中,在遠程返回資料前也需要經過一些處理才返回資料,遠程輸出資料後將取決於網路本身的速度及資料本身大小。
階段2:返回資料首先被填充到核心(Kernel)的緩衝區裡,然後再從核心區將資料向進程內部拷貝(copy data from kernel to user)。這個過程完成後,應用程式才會繼續向下執行。
根據上面的過程,在BIO過程中,程式與核心互動的過程如所示:
2. NIO (New I/O)
Java從1.4版本開始支援NIO(New IO),可以實現非阻塞I/O的。非阻塞I/O和阻塞I/O有個很明顯的區別是:當發生第一次System Call請求後,線程並沒有被阻塞,但它沒有做別的事情,而是在不斷髮起System Call請求。
這樣操作似乎在空耗CPU,給人感覺還不如一直掛起等待返回,那樣至少會讓出CPU資源。其實,每次System Call只是看看資料準備好沒有,通常它的時間是很短暫的,這樣的動作完全只用一個線程來完成對很多事件的監聽。換句話說,其它的線程可以去幹別的事,只需要某個線程定期來做下檢測即可,在設計上注意這個檢測頻率,就可以達到效率高且節約資源的目的。
3. AIO (Asynchronous I/O)
JDK 1.7引入了NIO 2.0,也就是AIO,又叫非同步IO。下面用“物流送貨”來比如Java中的IO模型:
① BIO:你需要到物流的中轉站去等貨且不能離開中轉站,如果貨沒到,其他的事情就別想做。
② NIO:每天去檢測一下貨物是否到了,這個動作十分簡單,可以讓每個小區派1個人去看看有沒有這個小區的貨物,如果有就帶回來,或者這個人通知你去物流中轉站拿貨。
③ AIO:貨到的時候送貨上門,換句話說,去拿貨的路途雖然不長,但是由別人幫你承擔了。
使用AIO讀取檔案並不代表讀取更快,我們不能用AIO讀取一個大檔案與用BIO來對比速度,這樣沒有任何意義。因為AIO的目的在於I/O過程中程式去做別的事情,也就是在並發時,更少的資源可以做更多的事情,並不是看在I/O過程中誰更快,它也做不到這點,因為能做到I/O更快只有提高磁碟或者網路本身的速度,而這些I/O模型只是調度I/O的機制而已。
AIO相對NIO,在某種意義上講更加提高了資源使用率,但這僅僅是相對進程本身而言,對於整個伺服器來說,還得看實際情況,因為進程不想做的事情交給核心去做了。AIO應當更加適合運用在I/O密集型系統中,BIO也並非沒有價值,它使得互動簡化,而且在很多情況下本身沒有必要使用非同步和非阻塞。
Java BIO, NIO, AIO的一些粗淺認識