1. 程式人生 > >非同步和非阻塞一樣嗎? (內容涉及BIO,NIO,AIO,Netty)

非同步和非阻塞一樣嗎? (內容涉及BIO,NIO,AIO,Netty)

寫在開頭:

本文不是純講技術,會涉及概念理解和語義分析的要點。正如博主標題說的,高效的學習方法比擁有多少知識重要。高效的學習方法途徑之一就是閱讀理解,理解能力越好,掌握越快,知識體系更清晰。

如果剛入門Netty,看過Netty執行緒模型,又看過JDK的NIO、NIO2,是否困惑於NIO,AIO,阻塞非阻塞的理解?

Netty目前4.0的版本應該是“非阻塞”的“同步IO”(按照Unix IO模型定義)。之前曾經有版本支援“非同步IO”(按照Unix IO模型定義),但是因為某些原因被後來版本移除了。不管Netty是“同步IO”還是“非同步IO”(按照Unix IO模型定義),其實我們應用的層面是不需要區分的,因為這是框架底層封裝處理。我們應該僅僅關心是我的業務客戶端程式碼呼叫Netty的API時候客戶端程式是否會停頓等待。

“不停頓等待”

廣義的非同步,非阻塞,都可以直接理解為正在執行的執行緒“不停頓等待”.

當下面業務端main執行緒執行callNettyApi()時候,不管callNettyApi()執行是否完成,執行緒繼續執行至callBusinessHandle(),通俗說就是該main方法的執行緒在執行callBusinessHandle()之前沒有停頓等待callNettyApi()執行結果。

業務端程式:

void main(){

callNettyApi(); // 非同步?非阻塞?

callBusinessHandle();

}

void callNettyApi(){
...
...//執行很長時間,例如IO操作 ... }

基於上述的語境,用”不停頓等待”的詞不夠專業,於是很多人專業術語叫”非同步”。然後叫”非阻塞”如何呢?其實也對啊,就是在執行callNettyApi()方法時候沒有阻塞main執行緒。

至此,作為普通應用開發者呼叫Netty的API,無非就是關心自己呼叫的API方法是否讓自己的主執行緒停頓等待API執行結果,這種場景下是其實不需要考慮非同步和非阻塞的區別的,叫非同步也好,叫非阻塞也好。也就是廣義的非同步和非阻塞等同於”不停頓等待”。

“停頓等待”

廣義的同步,阻塞,都可以直接理解為正在執行的執行緒“停頓等待”.

這個不詳細舉例說明了,就是上面例子的相反理解就行。

轉入正題

文章開始就說了截止到Netty4.0版本是“非阻塞”的“同步IO”(按照Unix IO模型定義),同步與非阻塞矛盾?

答案是不矛盾。因為這句話要結合具體語境理解。

首先,在Unix的IO模型裡:

  • 非同步I/O 是指使用者程式發起IO請求後,不等待資料,同時作業系統核心負責I/O操作把資料從核心拷貝到使用者程式的緩衝區後通知應用程式。資料拷貝是由作業系統核心完成,使用者程式從一開始就沒有等待資料,發起請求後不參與任何IO操作,等核心通知完成。

  • 同步I/O 就是非非同步IO的情況,也就是使用者程式要參與把資料拷貝到程式緩衝區(例如java的InputStream讀位元組流過程)。

  • 同步IO裡的非阻塞 是指使用者程式發起IO操作請求後不等待資料,而是呼叫會立即返回一個標誌資訊告知條件不滿足,資料未準備好,從而使用者請求程式繼續執行其它任務。執行完其它任務,使用者程式會主動輪詢檢視IO操作條件是否滿足,如果滿足,則使用者程式親自參與拷貝資料動作。

這裡寫圖片描述

這裡寫圖片描述

簡單說,Unix IO模型的語境下,同步和非同步的區別在於資料拷貝階段是否需要完全由作業系統處理。阻塞和非阻塞操作是針對發起IO請求操作後是否有立刻返回一個標誌資訊而不讓請求執行緒等待。基於這個語境,Netty目前的版本是沒有把IO操作交過作業系統處理的,所以是屬於同步的。對於網上大部分文章,如果別人說Netty是非同步非阻塞,如果要深究,那真要看看Netty新的版本是否把IO操作交過作業系統處理,或者看看有否使用JDK1.7中的AIO API,否則他們說的非同步其實是指客戶端程式呼叫Netty的IO操作API“不停頓等待”

=====

其次,在Java的IO模型裡:
JDK的各個歷史版本引入如下概念。
BIO: Blocking IO
NIO:Non Blocking IO 或者 New IO
AIO:Asynchronous IO 或者 NIO2

這裡的AIO應該就對應著Unix模型裡的非同步IO,也就是IO操作交給作業系統處理,充分呼叫OS參與併發操作,確實是作業系統的非同步IO機制。

而BIO和NIO簡單對比就是,NIO解決了BIO的痛點,把BIO中請求IO過程中的兩步(請求連線+連線有真實IO請求時候的處理過程)分離開來,不讓一個執行緒負責這兩步。NIO就是一個執行緒負責所有請求連線但不處理IO操作,該執行緒只負責把連線註冊到多路複用器上,多用複用器輪詢到連線有IO請求時候再啟動其它執行緒處理IO請求操作。

注意一點,Netty執行緒模型提及很多IO執行緒池,每條IO執行緒在進行IO操作(IO條件滿足進行真正讀寫資料)時候雖然也是要消耗操作時間,但這種情況是否應該叫阻塞,取決於該IO執行緒有沒有阻塞業務請求執行緒,當且僅當所有的IO執行緒在重度負載情形下(IO執行緒池所有IO執行緒在工作)導致業務請求執行緒提交不了新請求的情形下才叫IO執行緒的IO操作阻塞了業務執行緒的IO請求。由此可知,NIO也會產生BIO的情況。

啟示

平時專案裡,溝通效率低效,往往也是不同的人描述不同的東西時候用了相同的詞彙,或者相同的東西卻用了不同的詞彙,這都會造成雙方理解困難。要高效溝通,其中途徑之一就是統一概念叫法。不允許重要而難懂的概念被隨意指代。

延伸, 那麼你是否好奇程式如何實現非阻塞的?

建議看看Java的Future模式實現和JDK併發包裡的Future工具庫。IO操作不阻塞使用者請求執行緒,程式設計可以使用Future模式實現。

結束語

希望本文幫你理解清楚這些概念,然後隨心應用Netty或者進行網路程式設計,無所顧慮自己的困惑影響到程式編寫的正確性。