阻塞和非阻塞_異步和同步

 
簡要

     阻塞和非阻塞,異步和同步的原理,以及java和linux在這方面的實現原理。

 

非阻塞和阻塞

     java io和nio涉及到阻塞和非阻塞,如select,read,write均存在阻塞和非阻塞的版本。 實現原理上,java依賴於低層linux的實現,所以原理是和linux的socket實現一致的,java只是封裝。 在大部分c/c++程序員眼裏,他們一定不明白爲什麼java要分爲io和nio這2個包,因爲他們熟悉的socket api,從一開始就有阻塞和非阻塞的概念,只是一個Socket選項的true和false的區別而已。

     直接看linux源代碼,能瞭解實現阻塞和非阻塞的區別(以下代碼截自socket api的accept方法):



 
      

     SS_NBIO變量是標識插口操作是否阻塞線程,tsleep是使線程睡眠的函數。如果設置了SS_NBIO爲true(即值爲1),則在得不到數據時返回EWOULDBLOCK,如果設置了SS_NBIO爲false(即 值爲 0),則在得不到數據時調用tsleep掛起等待。
     看吧,阻塞和非阻塞的accept就是這點區別。 如果有興趣想了解tsleep的具體細節,可以看看 refer (來自《HttpClient分享.ppt》第9頁到第16頁), 看了之後就回明白"阻塞/喚醒的原理"。

 

異步和同步

    異步和同步的區別是什麼?網上有許多 不錯的答案 ,本質說的是通知機制的差異:" 同步和異步僅僅是關於所關注的消息如何通知的機制,而不是處理消息的機制. 同步的情況下,是由處理消息者自己去等待消息是否被觸發,而異步的情況下是由觸發機制來通知處理消息者"

同步 就是自己去等待消息,舉例:
(1)通過阻塞自己,等待消息的到來
inputstream.read();
在調用 read時,應用程序會阻塞。當響應返回時(從我們正在從中讀取的設備中返回),程序就會解除阻塞(read 調用返回)。

(2)非阻塞,但通過輪詢,直到消息的到來
int count = socketChannel.read(byteBuffer);
while(count == 0){
    count = socketChannel.read(byteBuffer);
}
程序必須要進行忙碌等待,直到數據可用爲止。

異步 就是使用某種觸發機制來通知處理消息者 ,下面看看java的異步IO和linux的異步IO:

 

 Java的異步IO

      "使用某種觸發機制來通知消息處理者",如果你做過GUI編程,肯定對觀察者模式(Observer)很熟悉,JAVA的使用了Reactor模式,結合多線程,實現了異步通知的機制。個人覺得Reactor模式實際就是觀察者模式。例子如下:


 

     網上大部分文章把這種模式歸類爲異步阻塞IO。不過這種模式裏,既使用了阻塞的select方法,也使用了非阻塞的read/writre方法。將其稱爲 異 步阻塞,可能因爲使用了阻塞的select方法的線程屬於這個模式裏主要的線程,所以稱爲"阻塞"。不過我們還是得記住,這種模式的特點是 "使用reactor模式和多線程實現異步的通知機制,主線程使用阻塞的select監聽IO事件,其他線程使用非阻塞的read/write進行具體的事件處理"

 

 

 Linux的異步IO

    拋開Java,我們也想看看linux中的異步IO是怎麼實現的? 
   linux的異步I/0有2種:使用O_ASYNC標誌 和 使用aio.h

 

一 使用O_ASYNC標誌
   設置了O_ASYNC標誌(該標誌表示插口應該接收I/O事件的異步通知),當數據就緒時,內核會向"與F_SETOWN綁定的進程"發送SIGIO信號。(個人沒研究過這種方法)

 

二 使用aio.h
   linux2.5內核一些版本開始出現AIO,linux2.6則把AIO納入了標準。
   aio 的系列方法包括aio_read,aio_write等。這系列方法調用時需要傳遞一個結構體參數(struct)。當IO事件響應時,就會"產生一個信 號"或"執行一個回調函數" 來完成這次 I/O 處理過程。這裏,選擇"產生一個信號"或"執行一個回調函數" ,可以在結構體參數裏設置。
   簡單地可以這樣解釋這系列的aio方法: aio_xx(參數:信號處理函數 或 回調函數)


(1)使用信號進行異步通知

使用信號進行進程間通信(IPC)是 UNIX 中的一種傳統機制,AIO 也可以支持這種機制。應用程序需要定義信號處理程序,在產生指定的信號時就會調用這個處理程序。應用程序然後配置一個異步請求將在請求完成時產生一個信號


(2)使用回調函數進行異步通知
這種機制不會爲通知而產生一個信號,而是會調用用戶空間的一個函數來實現通知功能。

    看到回調函數,我又聯想到"java的reactor或觀察者模式"了,個人認爲,AIO也可以看成是使用了觀察者模式,不過AIO是在linux操作系 統層面實現,而java的異步需要使用reactor實現,比較費勁,這是因爲當時還沒有得到底層的AIO支持。

 

    目前看來,對於這些操作系統的特點,Java雖然無法超越而且有些落後,但是Java總是在努力:JDK7已經加入了異步I/O操作, 以Proactor模式爲原型設計,實現當然依附於上述的linux的AIO原理。

 

 

 

總結

    對於阻塞和非阻塞方面,到jdk1.6爲止(linux版),java的io,nio包,包含了阻塞和非阻塞的IO,是完全依賴於linux的,原理和 linux一致,linux的io有SS_NBIO選項(即可以選擇阻塞或非阻塞),而java則在1.0和1.4這2個版本分別實現阻塞和非阻塞。
    對於異步和同步方面,java流行的nio框架(jdk1.6基於linux的epoll)使用reactor模式和多線程實現了異步阻塞io,不過這個實現和linux的異步aio沒一點關 系;java7開始,添加了java aio的實現,原理就是基於linux的aio,java aio的出現,可能讓netty等nio框架要麼慢慢消失,要麼升級爲aio框架。

 

 

參考:
http://www.ibm.com/developerworks/cn/linux/l-async/
http://dev.firnow.com/course/6_system/linux/Linuxjs/20081012/150267.html
http://www.blogjava.net/killme2008/archive/2009/09/20/295743.html

http://www.iteye.com/articles/2813

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章