BIO與NIO、AIO的區別(這個容易理解)

 IO的方式一般分爲幾種,同步阻塞的BIO、同步非阻塞的NIO、異步非阻塞的AIO。編程

1、BIO後端

     在 JDK1.4出來以前,咱們創建網絡鏈接的時候採用BIO模式,須要先在服務端啓動一個ServerSocket,而後在客戶端啓動Socket來對服務 端進行通訊,默認狀況下服務端須要對每一個請求創建一堆線程等待請求,而客戶端發送請求後,先諮詢服務端是否有線程相應,若是沒有則會一直等待或者遭到拒絕 請求,若是有的話,客戶端會線程會等待請求結束後才繼續執行。緩存

2、NIO服務器

    NIO 自己是基於事件驅動思想來完成的,其主要想解決的是BIO的大併發問題: 在使用同步I/O的網絡應用中,若是要同時處理多個客戶端請求,或是在客戶端要同時和多個服務器進行通信,就必須使用多線程來處理。也就是說,將每個客 戶端請求分配給一個線程來單獨處理。這樣作雖然能夠達到咱們的要求,但同時又會帶來另一個問題。因爲每建立一個線程,就要爲這個線程分配必定的內存空間 (也叫工做存儲器),並且操做系統自己也對線程的總數有必定的限制。若是客戶端的請求過多,服務端程序可能會由於不堪重負而拒絕客戶端的請求,甚至服務器 可能會所以而癱瘓。網絡

    NIO基於Reactor,當socket有流可讀或可寫入socket時,操做系統會相應的通知引用程序進行處理,應用再將流讀取到緩衝區或寫入操做系 統。  也就是說,這個時候,已經不是一個鏈接就要對應一個處理線程了,而是有效的請求,對應一個線程,當鏈接沒有數據時,是沒有工做線程來處理的。多線程

   BIO與NIO一個比較重要的不一樣,是咱們使用BIO的時候每每會引入多線程,每一個鏈接一個單獨的線程;而NIO則是使用單線程或者只使用少許的多線程,每一個鏈接共用一個線程。架構

      NIO的最重要的地方是當一個鏈接建立後,不須要對應一個線程,這個鏈接會被註冊到多路複用器上面,因此全部的鏈接只須要一個線程就能夠搞定,當這個線程 中的多路複用器進行輪詢的時候,發現鏈接上有請求的話,纔開啓一個線程進行處理,也就是一個請求一個線程模式。併發

   HTTP/1.1出現後,有了Http長鏈接,這樣除了超時和指明特定關閉的http header外,這個連接是一直打開的狀態的,這樣在NIO處理中能夠進一步的進化,在後端資源中能夠實現資源池或者隊列,當請求來的話,開啓的線程把請 求和請求數據傳送給後端資源池或者隊列裏面就返回,而且在全局的地方保持住這個現場(哪一個鏈接的哪一個請求等),這樣前面的線程仍是能夠去接受其餘的請求, 然後端的應用的處理只須要執行隊列裏面的就能夠了,這樣請求處理和後端應用是異步的.當後端處理完,到全局地方獲得現場,產生響應,這個就實現了異步處 理。app

NIO主要有三大核心部分:Channel(通道),Buffer(緩衝區), Selector。傳統IO基於字節流和字符流進行操做,而NIO基於Channel和Buffer(緩衝區)進行操做,數據老是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。Selector(選擇區)用於監聽多個通道的事件(好比:鏈接打開,數據到達)。所以,單個線程能夠監聽多個數據通道。

NIO和傳統IO(一下簡稱IO)之間第一個最大的區別是,IO是面向流的,NIO是面向緩衝區的。 Java IO面向流意味着每次從流中讀一個或多個字節,直至讀取全部字節,它們沒有被緩存在任何地方。此外,它不能先後移動流中的數據。若是須要先後移動從流中讀取的數據,須要先將它緩存到一個緩衝區。NIO的緩衝導向方法略有不一樣。數據讀取到一個它稍後處理的緩衝區,須要時可在緩衝區中先後移動。這就增長了處理過程當中的靈活性。可是,還須要檢查是否該緩衝區中包含全部您須要處理的數據。並且,需確保當更多的數據讀入緩衝區時,不要覆蓋緩衝區裏還沒有處理的數據。

IO的各類流是阻塞的。這意味着,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些數據被讀取,或數據徹底寫入。該線程在此期間不能再幹任何事情了。 NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,可是它僅能獲得目前可用的數據,若是目前沒有數據可用時,就什麼都不會獲取。而不是保持線程阻塞,因此直至數據變的能夠讀取以前,該線程能夠繼續作其餘的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不須要等待它徹底寫入,這個線程同時能夠去作別的事情。 線程一般將非阻塞IO的空閒時間用於在其它通道上執行IO操做,因此一個單獨的線程如今能夠管理多個輸入和輸出通道

 

Channel

首先說一下Channel,國內大多翻譯成「通道」。Channel和IO中的Stream(流)是差很少一個等級的。只不過Stream是單向的,譬如:InputStream, OutputStream.而Channel是雙向的,既能夠用來進行讀操做,又能夠用來進行寫操做。
NIO中的Channel的主要實現有:

    FileChannel
    DatagramChannel
    SocketChannel
    ServerSocketChannel

這裏看名字就能夠猜出個因此然來:分別能夠對應文件IO、UDP和TCP(Server和Client)。下面演示的案例基本上就是圍繞這4個類型的Channel進行陳述的。
Buffer

NIO中的關鍵Buffer實現有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分別對應基本數據類型: byte, char, double, float, int, long, short。固然NIO中還有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer等這裏先不進行陳述。
Selector

Selector運行單線程處理多個Channel,若是你的應用打開了多個通道,但每一個鏈接的流量都很低,使用Selector就會很方便。例如在一個聊天服務器中。要使用Selector, 得向Selector註冊Channel,而後調用它的select()方法。這個方法會一直阻塞到某個註冊的通道有事件就緒。一旦這個方法返回,線程就能夠處理這些事件,事件的例子有如新的鏈接進來、數據接收等。

 

 

 

 

 

NIO是一個請求一個線程。異步

先來個例子理解一下概念,以銀行取款爲例: 

  • 異步 : 委託一小弟拿銀行卡到銀行取錢,而後給你(使用異步IO時,Java將IO讀寫委託給OS處理,須要將數據緩衝區地址和大小傳給OS(銀行卡和密碼),OS須要支持異步IO操做API);
  • 非 阻塞 : 櫃檯取款,取個號,而後坐在椅子上作其它事,等號廣播會通知你辦理,沒到號你就不能去,你能夠不斷問大堂經理排到了沒有,大堂經理若是說還沒到你就不能去 (使用非阻塞IO時,若是不能讀寫Java調用會立刻返回,當IO事件分發器會通知可讀寫時再繼續進行讀寫,不斷循環直到讀寫完成)

Java BIO : 同步並阻塞,服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷,固然能夠經過線程池機制改善。

  • Java AIO(NIO.2) : 異步非阻塞,服務器實現模式爲一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啓動線程進行處理,

BIO方式適用於鏈接數目比較小且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4之前的惟一選擇,但程序直觀簡單易理解。

  • AIO方式使用於鏈接數目多且鏈接比較長(重操做)的架構,好比相冊服務器,充分調用OS參與併發操做,編程比較複雜,JDK7開始支持。

在高性能的I/O設計中,有兩個比較著名的模式Reactor和Proactor模式,其中Reactor模式用於同步I/O,而Proactor運用於異步I/O操做。

 通常來講I/O模型能夠分爲:同步阻塞,同步非阻塞,異步阻塞,異步非阻塞IO

在此種方式下,用戶進程在發起一個IO操做之後,必須等待IO操做的完成,只有當真正完成了IO操做之後,用戶進程才能運行。JAVA傳統的IO模型屬於此種方式!

在此種方式下,用戶進程發起一個IO操做之後邊可返回作其它事情,可是用戶進程須要時不時的詢問IO操做是否就緒,這就要求用戶進程不停的去詢問,從而引入沒必要要的CPU資源浪費。其中目前JAVA的NIO就屬於同步非阻塞IO。

此 種方式下是指應用發起一個IO操做之後,不等待內核IO操做的完成,等內核完成IO操做之後會通知應用程序,這其實就是同步和異步最關鍵的區別,同步必須 等待或者主動的去詢問IO是否完成,那麼爲何說是阻塞的呢?由於此時是經過select系統調用來完成的,而select函數自己的實現方式是阻塞的, 而採用select函數有個好處就是它能夠同時監聽多個文件句柄,從而提升系統的併發性!

在 此種模式下,用戶進程只須要發起一個IO操做而後當即返回,等IO操做真正的完成之後,應用程序會獲得IO操做完成的通知,此時用戶進程只須要對數據進行 處理就行了,不須要進行實際的IO讀寫操做,由於真正的IO讀取或者寫入操做已經由內核完成了。目前Java中尚未支持此種IO模型

 

 

 

 

 

【當你用心寫完每一篇博客以後,你會發現它比你用代碼實現功能更有成就感!】
相關文章
相關標籤/搜索