1. 程式人生 > >阻塞非阻塞,同步非同步

阻塞非阻塞,同步非同步

也許很多朋友在學習NIO的時候都會感覺有點吃力,對裡面的很多概念都感覺不是那麼明朗。在進入Java NIO程式設計之前,我們今天先來討論一些比較基礎的知識:I/O模型。下面本文先從同步和非同步的概念 說起,然後接著闡述了阻塞和非阻塞的區別,接著介紹了阻塞IO和非阻塞IO的區別,然後介紹了同步IO和非同步IO的區別,接下來介紹了5種IO模型,最後介紹了兩種和高效能IO設計相關的設計模式(Reactor和Proactor)。

  以下是本文的目錄大綱:

  一.什麼是同步?什麼是非同步?

  二.什麼是阻塞?什麼是非阻塞?

  三.什麼是阻塞IO?什麼是非阻塞IO?

  四.什麼是同步IO?什麼是非同步IO?

  五.五種IO模型

  六.兩種高效能IO設計模式

  若有不正之處,請多多諒解並歡迎批評指正。

  請尊重作者勞動成果,轉載請標明原文連結:

   http://www.cnblogs.com/dolphin0520/p/3916526.html

一.什麼是同步?什麼是非同步?

  同步和非同步的概念出來已經很久了,網上有關同步和非同步的說法也有很多。以下是我個人的理解:

  同步就是:如果有多個任務或者事件要發生,這些任務或者事件必須逐個地進行,一個事件或者任務的執行會導致整個流程的暫時等待,這些事件沒有辦法併發地執行;

  非同步就是:如果有多個任務或者事件發生,這些事件可以併發地執行,一個事件或者任務的執行不會導致整個流程的暫時等待。

  這就是同步和非同步。舉個簡單的例子,假如有一個任務包括兩個子任務A和B,對於同步來說,當A在執行的過程中,B只有等待,直至A執行完畢,B才能執行;而對於非同步就是A和B可以併發地執行,B不必等待A執行完畢之後再執行,這樣就不會由於A的執行導致整個任務的暫時等待。

  如果還不理解,可以先看下面這2段程式碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 void  fun1() {            }        void  fun2() {            }        void  function(){        fun1();        fun2()        .....        .....    }

   這段程式碼就是典型的同步,在方法function中,fun1在執行的過程中會導致後續的fun2無法執行,fun2必須等待fun1執行完畢才可以執行。

  接著看下面這段程式碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void  fun1() {       }   void  fun2() {       }   void  function(){      new  Thread(){          public  void  run() {              fun1();          }      }.start();            new  Thread(){          public  void  run() {              fun2();          }      }.start();        .....      ..... }

   這段程式碼是一種典型的非同步,fun1的執行不會影響到fun2的執行,並且fun1和fun2的執行不會導致其後續的執行過程處於暫時的等待。

  事實上,同步和非同步是一個非常廣的概念,它們的重點在於多個任務和事件發生時,一個事件的發生或執行是否會導致整個流程的暫時等待。我覺得可以將同步和非同步與Java中的synchronized關鍵字聯絡起來進行類比。當多個執行緒同時訪問一個變數時,每個執行緒訪問該變數就是一個事件,對於同步來說,就是這些執行緒必須逐個地來訪問該變數,一個執行緒在訪問該變數的過程中,其他執行緒必須等待;而對於非同步來說,就是多個執行緒不必逐個地訪問該變數,可以同時進行訪問。

  因此,個人覺得同步和非同步可以表現在很多方面,但是記住其關鍵在於多個任務和事件發生時,一個事件的發生或執行是否會導致整個流程的暫時等待。一般來說,可以通過多執行緒的方式來實現非同步,但是千萬記住不要將多執行緒和非同步畫上等號,非同步只是巨集觀上的一個模式,採用多執行緒來實現非同步只是一種手段,並且通過多程序的方式也可以實現非同步。

二.什麼是阻塞?什麼是非阻塞?

  在前面介紹了同步和非同步的區別,這一節來看一下阻塞和非阻塞的區別。

  阻塞就是:當某個事件或者任務在執行過程中,它發出一個請求操作,但是由於該請求操作需要的條件不滿足,那麼就會一直在那等待,直至條件滿足;

  非阻塞就是:當某個事件或者任務在執行過程中,它發出一個請求操作,如果該請求操作需要的條件不滿足,會立即返回一個標誌資訊告知條件不滿足,不會一直在那等待。

  這就是阻塞和非阻塞的區別。也就是說阻塞和非阻塞的區別關鍵在於當發出請求一個操作時,如果條件不滿足,是會一直等待還是返回一個標誌資訊。

  舉個簡單的例子:

  假如我要讀取一個檔案中的內容,如果此時檔案中沒有內容可讀,對於同步來說就是會一直在那等待,直至檔案中有內容可讀;而對於非阻塞來說,就會直接返回一個標誌資訊告知檔案中暫時無內容可讀。

  在網上有一些朋友將同步和非同步分別與阻塞和非阻塞畫上等號,事實上,它們是兩組完全不同的概念。注意,理解這兩組概念的區別對於後面IO模型的理解非常重要。

  同步和非同步著重點在於多個任務的執行過程中,一個任務的執行是否會導致整個流程的暫時等待;

  而阻塞和非阻塞著重點在於發出一個請求操作時,如果進行操作的條件不滿足是否會返會一個標誌資訊告知條件不滿足。

  理解阻塞和非阻塞可以同線程阻塞類比地理解,當一個執行緒進行一個請求操作時,如果條件不滿足,則會被阻塞,即在那等待條件滿足。

三.什麼是阻塞IO?什麼是非阻塞IO?

  在瞭解阻塞IO和非阻塞IO之前,先看下一個具體的IO操作過程是怎麼進行的。

  通常來說,IO操作包括:對硬碟的讀寫、對socket的讀寫以及外設的讀寫。

  當用戶執行緒發起一個IO請求操作(本文以讀請求操作為例),核心會去檢視要讀取的資料是否就緒,對於阻塞IO來說,如果資料沒有就緒,則會一直在那等待,直到資料就緒;對於非阻塞IO來說,如果資料沒有就緒,則會返回一個標誌資訊告知使用者執行緒當前要讀的資料沒有就緒。當資料就緒之後,便將資料拷貝到使用者執行緒,這樣才完成了一個完整的IO讀請求操作,也就是說一個完整的IO讀請求操作包括兩個階段:

  1)檢視資料是否就緒;

  2)進行資料拷貝(核心將資料拷貝到使用者執行緒)。

  那麼阻塞(blocking IO)和非阻塞(non-blocking IO)的區別就在於第一個階段,如果資料沒有就緒,在檢視資料是否就緒的過程中是一直等待,還是直接返回一個標誌資訊。

  Java中傳統的IO都是阻塞IO,比如通過socket來讀資料,呼叫read()方法之後,如果資料沒有就緒,當前執行緒就會一直阻塞在read方法呼叫那裡,直到有資料才返回;而如果是非阻塞IO的話,當資料沒有就緒,read()方法應該返回一個標誌資訊,告知當前執行緒資料沒有就緒,而不是一直在那裡等待。

四.什麼是同步IO?什麼是非同步IO?

  我們先來看一下同步IO和非同步IO的定義,在《Unix網路程式設計》一書中對同步IO和非同步IO的定義是這樣的:

  A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes.
  An asynchronous I/O operation does not cause the requesting process to be blocked.

  從字面的意思可以看出:同步IO即 如果一個執行緒請求進行IO操作,在IO操作完成之前,該執行緒會被阻塞;

  而非同步IO為 如果一個執行緒請求進行IO操作,IO操作不會導致請求執行緒被阻塞。

  事實上,同步IO和非同步IO模型是針對使用者執行緒和核心的互動來說的:

  對於同步IO:當用戶發出IO請求操作之後,如果資料沒有就緒,需要通過使用者執行緒或者核心不斷地去輪詢資料是否就緒,當資料就緒時,再將資料從核心拷貝到使用者執行緒;

  而非同步IO:只有IO請求操作的發出是由使用者執行緒來進行的,IO操作的兩個階段都是由核心自動完成,然後傳送通知告知使用者執行緒IO操作已經完成。也就是說在非同步IO中,不會對使用者執行緒產生任何阻塞。

  這是同步IO和非同步IO關鍵區別所在,同步IO和非同步IO的關鍵區別反映在資料拷貝階段是由使用者執行緒完成還是核心完成。所以說非同步IO必須要有作業系統的底層支援。

  注意同步IO和非同步IO與阻塞IO和非阻塞IO是不同的兩組概念。

  阻塞IO和非阻塞IO是反映在當用戶請求IO操作時,如果資料沒有就緒,是使用者執行緒一直等待資料就緒,還是會收到一個標誌資訊這一點上面的。也就是說,阻塞IO和非阻塞IO是反映在IO操作的第一個階段,在檢視資料是否就緒時是如何處理的。

五.五種IO模型

  在《Unix網路程式設計》一書中提到了五種IO模型,分別是:阻塞IO、非阻塞IO、多路複用IO、訊號驅動IO以及非同步IO。

  下面就分別來介紹一下這5種IO模型的異同。

1.阻塞IO模型

  最傳統的一種IO模型,即在讀寫資料過程中會發生阻塞現象。

  當用戶執行緒發出IO請求之後,核心會去檢視資料是否就緒,如果沒有就緒就會等待資料就緒,而使用者執行緒就會處於阻塞狀態,使用者執行緒交出CPU。當資料就緒之後,核心會將資料拷貝到使用者執行緒,並返回結果給使用者執行緒,使用者執行緒才解除block狀態。

  典型的阻塞IO模型的例子為:

1 data = socket.read();

   如果資料沒有就緒,就會一直阻塞在read方法。

2.非阻塞IO模型

  當用戶執行緒發起一個read操作後,並不需要等待,而是馬上就得到了一個結果。如果結果是一個error時,它就知道資料還沒有準備好,於是它可以再次傳送read操作。一旦核心中的資料準備好了,並且又再次收到了使用者執行緒的請求,那麼它馬上就將資料拷貝到了使用者執行緒,然後返回。

  所以事實上,在非阻塞IO模型中,使用者執行緒需要不斷地詢問核心資料是否就緒,也就說非阻塞IO不會交出CPU,而會一直佔用CPU。

  典型的非阻塞IO模型一般如下:

1 2 3 4 5 6 7 while ( true ){      data = socket.read();      if (data!= error){          處理資料          break ;      } }

   但是對於非阻塞IO就有一個非常嚴重的問題,在while迴圈中需要不斷地去詢問核心資料是否就緒,這樣會導致CPU佔用率非常高,因此一般情況下很少使用while迴圈這種方式來讀取資料。

3.多路複用IO模型

  多路複用IO模型是目前使用得比較多的模型。Java NIO實際上就是多路複用IO。

  在多路複用IO模型中,會有一個執行緒不斷去輪詢多個socket的狀態,只有當socket真正有讀寫事件時,才真正呼叫實際的IO讀寫操作。因為在多路複用IO模型中,只需要使用一個執行緒就可以管理多個socket,系統不需要建立新的程序或者執行緒,也不必維護這些執行緒和程序,並且只有在真正有socket讀寫事件進行時,才會使用IO資源,所以它大大減少了資源佔用。

  在Java NIO中,是通過selector.select()去查詢每個通道是否有到達事件,如果沒有事件,則一直阻塞在那裡,因此這種方式會導致使用者執行緒的阻塞。

  也許有朋友會說,我可以採用 多執行緒+ 阻塞IO 達到類似的效果,但是由於在多執行緒 + 阻塞IO 中,每個socket對應一個執行緒,這樣會造成很大的資源佔用,並且尤其是對於長連線來說,執行緒的資源一直不會釋放,如果後面陸續有很多連線的話,就會造成效能上的瓶頸。

  而多路複用IO模式,通過一個執行緒就可以管理多個socket,只有當socket真正有讀寫事件發生才會佔用資源來進行實際的讀寫操作。因此,多路複用IO比較適合連線數比較多的情況。

  另外多路複用IO為何比非阻塞IO模型的效率高是因為在非阻塞IO中,不斷地詢問socket狀態時通過使用者執行緒去進行的,而在多路複用IO中,輪詢每個socket狀態是核心在進行的,這個效率要比使用者執行緒要高的多。

  不過要注意的是,多路複用IO模型是通過輪詢的方式來檢測是否有事件到達,並且對到達的事件逐一進行響應。因此對於多路複用IO模型來說,一旦事件響應體很大,那麼就會導致後續的事件遲遲得不到處理,並且會影響新的事件輪詢。

4.訊號驅動IO模型

  在訊號驅動IO模型中,當用戶執行緒發起一個IO請求操作,會給對應的socket註冊一個訊號函式,然後使用者執行緒會繼續執行,當核心資料就緒時會發送一個訊號給使用者執行緒,使用者執行緒接收到訊號之後,便在訊號函式中呼叫IO讀寫操作來進行實際的IO請求操作。

5.非同步IO模型

  非同步IO模型才是最理想的IO模型,在非同步IO模型中,當用戶執行緒發起read操作之後,立刻就可以開始去做其它的事。而另一方面,從核心的角度,當它受到一個asynchronous read之後,它會立刻返回,說明read請求已經成功發起了,因此不會對使用者執行緒產生任何block。然後,核心會等待資料準備完成,然後將資料拷貝到使用者執行緒,當這一切都完成之後,核心會給使用者執行緒傳送一個訊號,告訴它read操作完成了。也就說使用者執行緒完全不需要實際的整個IO操作是如何進行的,只需要先發起一個請求,當接收核心返回的成功訊號時表示IO操作已經完成,可以直接去使用資料了。

  也就說在非同步IO模型中,IO操作的兩個階段都不會阻塞使用者執行緒,這兩個階段都是由核心自動完成,然後傳送一個訊號告知使用者執行緒操作已完成。使用者執行緒中不需要再次呼叫IO函式進行具體的讀寫。這點是和訊號驅動模型有所不同的,在訊號驅動模型中,當用戶執行緒接收到訊號表示資料已經就緒,然後需要使用者執行緒呼叫IO函式進行實際的讀寫操作;而在非同步IO模型中,收到訊號表示IO操作已經完成,不需要再在使用者執行緒中呼叫iO函式進行實際的讀寫操作。

  注意,非同步IO是需要作業系統的底層支援,在Java 7中,提供了Asynchronous IO。

  前面四種IO模型實際上都屬於同步IO,只有最後一種是真正的非同步IO,因為無論是多路複用IO還是訊號驅動模型,IO操作的第2個階段都會引起使用者執行緒阻塞,也就是核心進行資料拷貝的過程都會讓使用者執行緒阻塞。

六.兩種高效能IO設計模式

  在傳統的網路服務設計模式中,有兩種比較經典的模式:

  一種是 多執行緒,一種是執行緒池。

  對於多執行緒模式,也就說來了client,伺服器就會新建一個執行緒來處理該client的讀寫事件,如下圖所示:

  這種模式雖然處理起來簡單方便,但是由於伺服器為每個client的連線都採用一個執行緒去處理,使得資源佔用非常大。因此,當連線數量達到上限時,再有使用者請求連線,直接會導致資源瓶頸,嚴重的可能會直接導致伺服器崩潰。

  因此,為了解決這種一個執行緒對應一個客戶端模式帶來的問題,提出了採用執行緒池的方式,也就說建立一個固定大小的執行緒池,來一個客戶端,就從執行緒池取一個空閒執行緒來處理,當客戶端處理完讀寫操作之後,就交出對執行緒的佔用。因此這樣就避免為每一個客戶端都要建立執行緒帶來的資源浪費,使得執行緒可以重用。

  但是執行緒池也有它的弊端,如果連線大多是長連線,因此可能會導致在一段時間內,執行緒池中的執行緒都被佔用,那麼當再有使用者請求連線時,由於沒有可用的空閒執行緒來處理,就會導致客戶端連線失敗,從而影響使用者體驗。因此,執行緒池比較適合大量的短連線應用。

  因此便出現了下面的兩種高效能IO設計模式:Reactor和Proactor。

  在Reactor模式中,會先對每個client註冊感興趣的事件,然後有一個執行緒專門去輪詢每個client是否有事件發生,當有事件發生時,便順序處理每個事件,當所有事件處理完之後,便再轉去繼續輪詢,如下圖所示:

  從這裡可以看出,上面的五種IO模型中的多路複用IO就是採用Reactor模式。注意,上面的圖中展示的 是順序處理每個事件,當然為了提高事件處理速度,可以通過多執行緒或者執行緒池的方式來處理事件。

  在Proactor模式中,當檢測到有事件發生時,會新起一個非同步操作,然後交由核心執行緒去處理,當核心執行緒完成IO操作之後,傳送一個通知告知操作已完成,可以得知,非同步IO模型採用的就是Proactor模式。

  參考資料:

  《Unix網路程式設計》

  http://blog.csdn.net/goldensuny/article/details/30717107

  http://my.oschina.net/XYleung/blog/295122

  http://xmuzyq.iteye.com/blog/783218

  http://www.cnblogs.com/ccdev/p/3542669.html

  http://alicsd.iteye.com/blog/868702

  http://www.smithfox.com/?e=191

  http://www.cnblogs.com/Anker/p/3254269.html

  http://blog.csdn.net/hguisu/article/details/7453390

  http://www.cnblogs.com/dawen/archive/2011/05/18/2050358.html

  

作者: 海子      出處: http://www.cnblogs.com/dolphin0520/      本部落格中未標明轉載的文章歸作者 海子和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。

也許很多朋友在學習NIO的時候都會感覺有點吃力,對裡面的很多概念都感覺不是那麼明朗。在進入Java NIO程式設計之前,我們今天先來討論一些比較基礎的知識:I/O模型。下面本文先從同步和非同步的概念 說起,然後接著闡述了阻塞和非阻塞的區別,接著介紹了阻塞IO和非阻塞IO的區別,然後介紹了同步IO和非同步IO的區別,接下來介紹了5種IO模型,最後介紹了兩種和高效能IO設計相關的設計模式(Reactor和Proactor)。

  以下是本文的目錄大綱:

  一.什麼是同步?什麼是非同步?

  二.什麼是阻塞?什麼是非阻塞?

  三.什麼是阻塞IO?什麼是非阻塞IO?

  四.什麼是同步IO?什麼是非同步IO?

  五.五種IO模型

  六.兩種高效能IO設計模式

  若有不正之處,請多多諒解並歡迎批評指正。

  請尊重作者勞動成果,轉載請標明原文連結:

   http://www.cnblogs.com/dolphin0520/p/3916526.html

一.什麼是同步?什麼是非同步?

  同步和非同步的概念出來已經很久了,網上有關同步和非同步的說法也有很多。以下是我個人的理解:

  同步就是:如果有多個任務或者事件要發生,這些任務或者事件必須逐個地進行,一個事件或者任務的執行會導致整個流程的暫時等待,這些事件沒有辦法併發地執行;

  非同步就是:如果有多個任務或者事件發生,這些事件可以併發地執行,一個事件或者任務的執行不會導致整個流程的暫時等待。

  這就是同步和非同步。舉個簡單的例子,假如有一個任務包括兩個子任務A和B,對於同步來說,當A在執行的過程中,B只有等待,直至A執行完畢,B才能執行;而對於非同步就是A和B可以併發地執行,B不必等待A執行完畢之後再執行,這樣就不會由於A的執行導致整個任務的暫時等待。

  如果還不理解,可以先看下面這2段程式碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 void  fun1() {            }        void  fun2() {            }        void  function(){        fun1();        fun2()        .....        .....    }

   這段程式碼就是典型的同步,在方法function中,fun1在執行的過程中會導致後續的fun2無法執行,fun2必須等待fun1執行完畢才可以執行。

  接著看下面這段程式碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void  fun1() {       }   void  fun2() {       }   void  function(){      new  Thread(){          public  void  run() {              fun1();          }      }.start();            new  Thread(){          public  void  run() {              fun2();          }      }.start();        .....      ..... }

   這段程式碼是一種典型的非同步,fun1的執行不會影響到fun2的執行,並且fun1和fun2的執行不會導致其後續的執行過程處於暫時的等待。

  事實上,同步和非同步是一個非常廣的概念,它們的重點在於多個任務和事件發生時,一個事件的發生或執行是否會導致整個流程的暫時等待。我覺得可以將同步和非同步與Java中的synchronized關鍵字聯絡起來進行類比。當多個執行緒同時訪問一個變數時,每個執行緒訪問該變數就是一個事件,對於同步來說,就是這些執行緒必須逐個地來訪問該變數,一個執行緒在訪問該變數的過程中,其他執行緒必須等待;而對於非同步來說,就是多個執行緒不必逐個地訪問該變數,可以同時進行訪問。

  因此,個人覺得同步和非同步可以表現在很多方面,但是記住其關鍵在於多個任務和事件發生時,一個事件的發生或執行是否會導致整個流程的暫時等待。一般來說,可以通過多執行緒的方式來實現非同步,但是千萬記住不要將多執行緒和非同步畫上等號,非同步只是巨集觀上的一個模式,採用多執行緒來實現非同步只是一種手段,並且通過多程序的方式也可以實現非同步。

二.什麼是阻塞?什麼是非阻塞?

  在前面介紹了同步和非同步的區別,這一節來看一下阻塞和非阻塞的區別。

  阻塞就是:當某個事件或者任務在執行過程中,它發出一個請求操作,但是由於該請求操作需要的條件不滿足,那麼就會一直在那等待,直至條件滿足;

  非阻塞就是:當某個事件或者任務在執行過程中,它發出一個請求操作,如果該請求操作需要的條件不滿足,會立即返回一個標誌資訊告知條件不滿足,不會一直在那等待。

  這就是阻塞和非阻塞的區別。也就是說阻塞和非阻塞的區別關鍵在於當發出請求一個操作時,如果條件不滿足,是會一直等待還是返回一個標誌資訊。

  舉個簡單的例子:

  假如我要讀取一個檔案中的內容,如果此時檔案中沒有內容可讀,對於同步來說就是會一直在那等待,直至檔案中有內容可讀;而對於非阻塞來說,就會直接返回一個標誌資訊告知檔案中暫時無內容可讀。

  在網上有一些朋友將同步和非同步分別與阻塞和非阻塞畫上等號,事實上,它們是兩組完全不同的概念。注意,理解這兩組概念的區別對於後面IO模型的理解非常重要。

  同步和非同步著重點在於多個任務的執行過程中,一個任務的執行是否會導致整個流程的暫時等待;

  而阻塞和非阻塞著重點在於發出一個請求操作時,如果進行操作的條件不滿足是否會返會一個標誌資訊告知條件不滿足。

  理解阻塞和非阻塞可以同線程阻塞類比地理解,當一個執行緒進行一個請求操作時,如果條件不滿足,則會被阻塞,即在那等待條件滿足。

三.什麼是阻塞IO?什麼是非阻塞IO?

  在瞭解阻塞IO和非阻塞IO之前,先看下一個具體的IO操作過程是怎麼進行的。

  通常來說,IO操作包括:對硬碟的讀寫、對socket的讀寫以及外設的讀寫。

  當用戶執行緒發起一個IO請求操作(本文以讀請求操作為例),核心會去檢視要讀取的資料是否就緒,對於阻塞IO來說,如果資料沒有就緒,則會一直在那等待,直到資料就緒;對於非阻塞IO來說,如果資料沒有就緒,則會返回一個標誌資訊告知使用者執行緒當前要讀的資料沒有就緒。當資料就緒之後,便將資料拷貝到使用者執行緒,這樣才完成了一個完整的IO讀請求操作,也就是說一個完整的IO讀請求操作包括兩個階段:

  1)檢視資料是否就緒;

  2)進行資料拷貝(核心將資料拷貝到使用者執行緒)。

  那麼阻塞(blocking IO)和非阻塞(non-blocking IO)的區別就在於第一個階段,如果資料沒有就緒,在檢視資料是否就緒的過程中是一直等待,還是直接返回一個標誌資訊。

  Java中傳統的IO都是阻塞IO,比如通過socket來讀資料,呼叫read()方法之後,如果資料沒有就緒,當前執行緒就會一直阻塞在read方法呼叫那裡,直到有資料才返回;而如果是非阻塞IO的話,當資料沒有就緒,read()方法應該返回一個標誌資訊,告知當前執行緒資料沒有就緒,而不是一直在那裡等待。

四.什麼是同步IO?什麼是非同步IO?

  我們先來看一下同步IO和非同步IO的定義,在《Unix網路程式設計》一書中對同步IO和非同步IO的定義是這樣的:

  A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes.
  An asynchronous I/O operation does not cause the requesting process to be blocked.

  從字面的意思可以看出:同步IO即 如果一個執行緒請求進行IO操作,在IO操作完成之前,該執行緒會被阻塞;

  而非同步IO為 如果一個執行緒請求進行IO操作,IO操作不會導致請求執行緒被阻塞。

  事實上,同步IO和非同步IO模型是針對使用者執行緒和核心的互動來說的:

  對於同步IO:當用戶發出IO請求操作之後,如果資料沒有就緒,需要通過使用者執行緒或者核心不斷地去輪詢資料是否就緒,當資料就緒時,再將資料從核心拷貝到使用者執行緒;

  而非同步IO:只有IO請求操作的發出是由使用者執行緒來進行的,IO操作的兩個階段都是由核心自動完成,然後傳送通知告知使用者執行緒IO操作已經完成。也就是說在非同步IO中,不會對使用者執行緒產生任何阻塞。

  這是同步IO和非同步IO關鍵區別所在,同步IO和非同步IO的關鍵區別反映在資料拷貝階段是由使用者執行緒完成還是核心完成。所以說非同步IO必須要有作業系統的底層支援。

  注意同步IO和非同步IO與阻塞IO和非阻塞IO是不同的兩組概念。

  阻塞IO和非阻塞IO是反映在當用戶請求IO操作時,如果資料沒有就緒,是使用者執行緒一直等待資料就緒,還是會收到一個標誌資訊這一點上面的。也就是說,阻塞IO和非阻塞IO是反映在IO操作的第一個階段,在檢視資料是否就緒時是如何處理的。

五.五種IO模型

  在《Unix網路程式設計》一書中提到了五種IO模型,分別是:阻塞IO、非阻塞IO、多路複用IO、訊號驅動IO以及非同步IO。

  下面就分別來介紹一下這5種IO模型的異同。

1.阻塞IO模型

  最傳統的一種IO模型,即在讀寫資料過程中會發生阻塞現象。

  當用戶執行緒發出IO請求之後,核心會去檢視資料是否就緒,如果沒有就緒就會等待資料就緒,而使用者執行緒就會處於阻塞狀態,使用者執行緒交出CPU。當資料就緒之後,核心會將資料拷貝到使用者執行緒,並返回結果給使用者執行緒,使用者執行緒才解除block狀態。

  典型的阻塞IO模型的例子為:

1 data = socket.read();

   如果資料沒有就緒,就會一直阻塞在read方法。

2.非阻塞IO模型

  當用戶執行緒發起一個read操作後,並不需要等待,而是馬上就得到了一個結果。如果結果是一個error時,它就知道資料還沒有準備好,於是它可以再次傳送read操作。一旦核心中的資料準備好了,並且又再次收到了使用者執行緒的請求,那麼它馬上就將資料拷貝到了使用者執行緒,然後返回。

  所以事實上,在非阻塞IO模型中,使用者執行緒需要不斷地詢問核心資料是否就緒,也就說非阻塞IO不會交出CPU,而會一直佔用CPU。

  典型的非阻塞IO模型一般如下:

1 2 3 4 5 6 7 while ( true ){      data = socket.read();      if (data!= error){          處理資料          break ;      } }

   但是對於非阻塞IO就有一個非常嚴重的問題,在while迴圈中需要不斷地去詢問核心資料是否就緒,這樣會導致CPU佔用率非常高,因此一般情況下很少使用while迴圈這種方式來讀取資料。

3.多路複用IO模型

  多路複用IO模型是目前使用得比較多的模型。Java NIO實際上就是多路複用IO。

  在多路複用IO模型中,會有一個執行緒不斷去輪詢多個socket的狀態,只有當socket真正有讀寫事件時,才真正呼叫實際的IO讀寫操作。因為在多路複用IO模型中,只需要使用一個執行緒就可以管理多個socket,系統不需要建立新的程序或者執行緒,也不必維護這些執行緒和程序,並且只有在真正有socket讀寫事件進行時,才會使用IO資源,所以它大大減少了資源佔用。

  在Java NIO中,是通過selector.select()去查詢每個通道是否有到達事件,如果沒有事件,則一直阻塞在那裡,因此這種方式會導致使用者執行緒的阻塞。

  也許有朋友會說,我可以採用 多執行緒+ 阻塞IO 達到類似的效果,但是由於在多執行緒 + 阻塞IO 中,每個socket對應一個執行緒,這樣會造成很大的資源佔用,並且尤其是對於長連線來說,執行緒的資源一直不會釋放,如果後面陸續有很多連線的話,就會造成效能上的瓶頸。

  而多路複用IO模式,通過一個執行緒就可以管理多個socket,只有當socket真正有讀寫事件發生才會佔用資源來進行實際的讀寫操作。因此,多路複用IO比較適合連線數比較多的情況。

  另外多路複用IO為何比非阻塞IO模型的效率高是因為在非阻塞IO中,不斷地詢問socket狀態時通過使用者執行緒去進行的,而在多路複用IO中,輪詢每個socket狀態是核心在進行的,這個效率要比使用者執行緒要高的多。

  不過要注意的是,多路複用IO模型是通過輪詢的方式來檢測是否有事件到達,並且對到達的事件逐一進行響應。因此對於多路複用IO模型來說,一旦事件響應體很大,那麼就會導致後續的事件遲遲得不到處理,並且會影響新的事件輪詢。

4.訊號驅動IO模型

  在訊號驅動IO模型中,當用戶執行緒發起一個IO請求操作,會給對應的socket註冊一個訊號函式,然後使用者執行緒會繼續執行,當核心資料就緒時會發送一個訊號給使用者執行緒,使用者執行緒接收到訊號之後,便在訊號函式中呼叫IO讀寫操作來進行實際的IO請求操作。

5.非同步IO模型

  非同步IO模型才是最理想的IO模型,在非同步IO模型中,當用戶執行緒發起read操作之後,立刻就可以開始去做其它的事。而另一方面,從核心的角度,當它受到一個asynchronous read之後,它會立刻返回,說明read請求已經成功發起了,因此不會對使用者執行緒產生任何block。然後,核心會等待資料準備完成,然後將資料拷貝到使用者執行緒,當這一切都完成之後,核心會給使用者執行緒傳送一個訊號,告訴它read操作完成了。也就說使用者執行緒完全不需要實際的整個IO操作是如何進行的,只需要先發起一個請求,當接收核心返回的成功訊號時表示IO操作已經完成,可以直接去使用資料了。

  也就說在非同步IO模型中,IO操作的兩個階段都不會阻塞使用者執行緒,這兩個階段都是由核心自動完成,然後傳送一個訊號告知使用者執行緒操作已完成。使用者執行緒中不需要再次呼叫IO函式進行具體的讀寫。這點是和訊號驅動模型有所不同的,在訊號驅動模型中,當用戶執行緒接收到訊號表示資料已經就緒,然後需要使用者執行緒呼叫IO函式進行實際的讀寫操作;而在非同步IO模型中,收到訊號表示IO操作已經完成,不需要再在使用者執行緒中呼叫iO函式進行實際的讀寫操作。

  注意,非同步IO是需要作業系統的底層支援,在Java 7中,提供了Asynchronous IO。

  前面四種IO模型實際上都屬於同步IO,只有最後一種是真正的非同步IO,因為無論是多路複用IO還是訊號驅動模型,IO操作的第2個階段都會引起使用者執行緒阻塞,也就是核心進行資料拷貝的過程都會讓使用者執行緒阻塞。

六.兩種高效能IO設計模式

  在傳統的網路服務設計模式中,有兩種比較經典的模式:

  一種是 多執行緒,一種是執行緒池。

  對於多執行緒模式,也就說來了client,伺服器就會新建一個執行緒來處理該client的讀寫事件,如下圖所示:

  這種模式雖然處理起來簡單方便,但是由於伺服器為每個client的連線都採用一個執行緒去處理,使得資源佔用非常大。因此,當連線數量達到上限時,再有使用者請求連線,直接會導致資源瓶頸,嚴重的可能會直接導致伺服器崩潰。

  因此,為了解決這種一個執行緒對應一個客戶端模式帶來的問題,提出了採用執行緒池的方式,也就說建立一個固定大小的執行緒池,來一個客戶端,就從執行緒池取一個空閒執行緒來處理,當客戶端處理完讀寫操作之後,就交出對執行緒的佔用。因此這樣就避免為每一個客戶端都要建立執行緒帶來的資源浪費,使得執行緒可以重用。

  但是執行緒池也有它的弊端,如果連線大多是長連線,因此可能會導致在一段時間內,執行緒池中的執行緒都被佔用,那麼當再有使用者請求連線時,由於沒有可用的空閒執行緒來處理,就會導致客戶端連線失敗,從而影響使用者體驗。因此,執行緒池比較適合大量的短連線應用。

  因此便出現了下面的兩種高效能IO設計模式:Reactor和Proactor。

  在Reactor模式中,會先對每個client註冊感興趣的事件,然後有一個執行緒專門去輪詢每個client是否有事件發生,當有事件發生時,便順序處理每個事件,當所有事件處理完之後,便再轉去繼續輪詢,如下圖所示:

  從這裡可以看出,上面的五種IO模型中的多路複用IO就是採用Reactor模式。注意,上面的圖中展示的 是順序處理每個事件,當然為了提高事件處理速度,可以通過多執行緒或者執行緒池的方式來處理事件。

  在Proactor模式中,當檢測到有事件發生時,會新起一個非同步操作,然後交由核心執行緒去處理,當核心執行緒完成IO操作之後,傳送一個通知告知操作已完成,可以得知,非同步IO模型採用的就是Proactor模式。

  參考資料:

  《Unix網路程式設計》

  http://blog.csdn.net/goldensuny/article/details/30717107

  http://my.oschina.net/XYleung/blog/295122

  

相關推薦

同步非同步阻塞阻塞區別聯絡

  實際上同步與非同步是針對應用程式與核心的互動而言的。同步過程中程序觸發IO操作並等待(也就是我們說的阻塞)或者輪詢的去檢視IO操作(也就是我們說的非阻塞)是否完成。 非同步過程中程序觸發IO操作以後,直接返回,做自己的事情,IO交給核心來處理,完成後核心通知程序IO完成。

同步非同步阻塞阻塞概念

對於網路程式設計,經常會遇到同步阻塞,同步非阻塞,非同步阻塞,非同步非阻塞這樣的字眼,對於阻塞這個詞,很好理解,從字面的意思上就可以看出阻塞的含義,但是,對於同步,非同步,如果單獨理解,也很好理解,就是時間上的關係。 舉個例子就是,我去燉了一壺水,我在旁邊等著,水一開,我立馬就聽到了水開

阻塞阻塞同步非同步

也許很多朋友在學習NIO的時候都會感覺有點吃力,對裡面的很多概念都感覺不是那麼明朗。在進入Java NIO程式設計之前,我們今天先來討論一些比較基礎的知識:I/O模型。下面本文先從同步和非同步的概念 說起,然後接著闡述了阻塞和非阻塞的區別,接著介紹了阻塞IO和非阻塞IO的區別,然後介紹了同步IO和非同步

組塞式阻塞同步非同步

請求描述: `阻塞/非阻塞` 和 `同步/非同步` 不是一個概念。舉幾個簡單的例子。          當程序呼叫一個進行IO操作的API時(比如read函式),在資料沒有到達前,read 會掛起,程序會卡住。在資料讀取完畢返回給

關於併發/並行阻塞/阻塞同步/非同步及程序/執行緒的理解

1. 阻塞,非阻塞           一個執行緒/程序經歷的5個狀態,建立,就緒,執行,阻塞,終止。各個狀態的轉換條件如上圖,其中有個阻塞狀態,就是說當執行緒中呼叫某個函式,需要IO請求,或者暫時得不到競爭資源的,作業系統會把該執行緒

深入理解併發/並行阻塞/阻塞同步/非同步 同步阻塞非同步阻塞的區別

同步與非同步是對應的,它們是執行緒之間的關係,兩個執行緒之間要麼是同步的,要麼是非同步的。阻塞與非阻塞是對同一個執行緒來說的,在某個時刻,執行緒要麼處於阻塞,要麼處於非阻塞。阻塞是使用同步機制的結果,非阻塞則是使用非同步機制的結果。 深入理解併發/並行,阻塞/非阻塞,同步/非同步 1. 阻塞,非

深入瞭解幾種IO模型(阻塞阻塞同步非同步

一般來說,Linux下系統IO主要就是通過以下幾個函式open(),close(),read(),write(),send(),recv(),lseek(),今天就以recv()為例來介紹下IO模型中的同步非同步,阻塞非阻塞的區別。先說阻塞與非阻塞的區別,recv()函式預設

深入理解併發/並行阻塞/阻塞同步/非同步

1. 阻塞,非阻塞 首先,阻塞這個詞來自作業系統的執行緒/程序的狀態模型中,如下圖: 一個執行緒/程序經歷的5個狀態,建立,就緒,執行,阻塞,終止。各個狀態的轉換條件如上圖,其中有個阻塞狀態,就是說當執行緒中呼叫某個函式,需要IO請求,或者暫時得不到競爭

MPI同步/非同步阻塞/阻塞通訊集合通訊Gather/Scatter

1. MPI MPI(Message Passing Interface)是一種訊息傳遞介面,是一個訊息傳遞漢書庫的標準說明。在基於MPI程式設計模型中,計算是由一個或多個彼此通過呼叫庫函式進行訊息收、發通訊的程序所組成。MPI為程式設計師提供一個並行環境庫,程式設計師通過

I/O 模型阻塞阻塞同步非同步

一個IO操作,需要2步。 以read為例,  read --> sys_Read --> 驅動中的buffer資料 OR 網路中的udp/tcp報文 等等 同步/非同步 是否阻塞在第二步。 也就是 sys_Read -->

linux下的 I/O 模型同步/非同步阻塞/阻塞介紹

同步/非同步,阻塞/非阻塞 一提到網路程式設計中的 I/O 模型,總會涉及到這幾個概念,但是這幾個名詞又容易混淆,於是我想總結一下。 我們先看一下在《UNIX網路程式設計:卷一》中講到的5中 UNIX 下的 I/O 模型,分別是 阻塞式 I/O 非阻塞式

同步非同步阻塞阻塞 詳細解釋

因為中文語意的問題,很多時候確實會導致混用,而且語境不一樣意義也可能不一樣。如果只是從計算機程式設計這個角度說, 討論最多的也是IO 模型 ,阻塞非阻塞 和 同步非同步說的應該是不同的東西。 阻塞非阻塞:可以簡單理解為需要做一件事能不能立即得到返回應答,如果不能立即獲得

Java深入理解併發/並行阻塞/阻塞同步/非同步

1. 阻塞,非阻塞 首先,阻塞這個詞來自作業系統的執行緒/程序的狀態模型中,如下圖: 一個執行緒/程序經歷的5個狀態,建立,就緒,執行,阻塞,終止。各個狀態的轉換條件如上圖,其中有個阻塞狀態,就是說當執行緒中呼叫

同步/異步阻塞/阻塞

其他 不同 網卡 div ted 框架 異步執行 ant 系統數據 什麽是同步和異步 同步和異步是針對應用程序和內核的交互而言的, 同步指的是用戶進程觸發IO操作並等待或者輪詢的去查看IO操作是否就緒,而異步是指用戶進程觸發IO操作以後便開始做自己的事情,而當IO操作已經完

深入理解並發/並行阻塞/阻塞同步/異步【轉】

非阻塞 n) 做的 耗時 非阻塞socket 衣服 時間片輪轉 輪轉 理解 1、阻塞,非阻塞 首先,阻塞這個詞來自操作系統的線程/進程的狀態模型中,如下圖: 一個線程/進程經歷的5個狀態,創建,就緒,運行,阻塞,終止。各個狀態的轉換條件如上圖,其中有個阻塞狀態,就是

超雞容易理解的---同步異步阻塞阻塞

點菜 擔心 窗口 aio 幫我 狀態 主動 書店 一段 快來看超雞容易理解的同步異步,阻塞非阻塞,再也不擔心理解晦澀的語言了。 阻塞和非阻塞指的是執行一個操作是等操作結束再返回,還是馬上返回。 比如餐館的服務員為用戶點菜,當有用戶點完菜後,服務員將菜單給後臺廚師,此時有兩種

深入理解並發/並行阻塞/阻塞同步/異步

text 只有一個 ima UNC 不同 比較 callback 調度 狀態 首先,阻塞這個詞來自操作系統的線程/進程的狀態模型中,如下圖 : 一個線程/進程經歷的5個狀態,創建,就緒,運行,阻塞,終止。各個狀態的轉換條件如上圖,其中有個阻塞狀態,就是說當線程中調用某個函

同步非同步阻塞阻塞

今天突然看到同步非同步和阻塞非阻塞的問題,一度陷入迷茫,經過我努力的汲取知識,在這裡說下我的理解。 同步非同步:可以理解成這是一種行為。 阻塞非阻塞:可以理解成這是一種狀態。 舉一個我看到的很不錯的例子: 《張大爺燒水》 話說張大爺有兩隻壺,一隻是普通的燒水壺,一隻是有響笛的

Java NIO/BIO 淺談 同步/非同步阻塞/阻塞

在我們瞭解Java NIO/BIO的網路通訊之前,我們先了解一下常用的阻塞/非阻塞模型以及同步/非同步的概念 一、阻塞和非阻塞 從簡單的開始,我們以經典的讀取檔案的模型舉例。(對作業系統而言,所有的輸入輸出裝置都被抽象成檔案。)在發起讀取檔案的請求時,應用層會呼叫系統核心的I/O介面。

Python併發程式設計之同步\非同步and阻塞\非阻塞

一、什麼是程序 程序: 正在進行的一個過程或者說一個任務。而負責執行任務則是cpu。 程序和程式的區別: 程式僅僅只是一堆程式碼而已,而程序指的是程式的執行過程。 需要強調的是:同一個程式執行兩次,那也是兩個程序,比如開啟暴風影音,雖然都是同一個軟體,但是一個可以播郭德綱,一個可以播高曉鬆。 二、並行