你真的理解BIO、NIO、AIO的區別嗎?

 

不少文章在談論到BIO、NIO、AIO的時候僅僅是拋出一堆定義,以及一些生動的例子。看似很好理解。可是並無將最基礎的本質原理顯現出來,若是沒有沒有從IO的原理出發的話是很難理解這三者之間的區別的。因此本篇文章從Java是如何進行IO操做爲開頭進行分析。java

Java中的IO原理

首先Java中的IO都是依賴操做系統內核進行的,咱們程序中的IO讀寫其實調用的是操做系統內核中的read&write兩大系統調用。windows

那內核是如何進行IO交互的呢?數組

  1. 網卡收到通過網線傳來的網絡數據,並將網絡數據寫到內存中。網絡

  2. 當網卡把數據寫入到內存後,網卡向cpu發出一箇中斷信號,操做系統便能得知有新數據到來,再經過網卡中斷程序去處理數據。併發

  3. 將內存中的網絡數據寫入到對應socket的接收緩衝區中。異步

  4. 當接收緩衝區的數據寫好以後,應用程序開始進行數據處理。socket

對應抽象到java的socket代碼簡單示例以下:ide

public class SocketServer {
  public static void main(String[] args) throws Exception {
    // 監聽指定的端口
    int port = 8080;
    ServerSocket server = new ServerSocket(port);
    // server將一直等待鏈接的到來
    Socket socket = server.accept();
    // 創建好鏈接後,從socket中獲取輸入流,並創建緩衝區進行讀取
    InputStream inputStream = socket.getInputStream();
    byte[] bytes = new byte[1024];
    int len;
    while ((len = inputStream.read(bytes)) != -1) {
      //獲取數據進行處理
      String message = new String(bytes, 0, len,"UTF-8");
    }
    // socket、server,流關閉操做,省略不表
  }
}

能夠看到這個過程和底層內核的網絡IO很相似,主要體如今accept()等待從網絡中的請求到來而後bytes[]數組做爲緩衝區等待數據填滿後進行處理。而BIO、NIO、AIO之間的區別就在於這些操做是同步仍是異步,阻塞仍是非阻塞。函數

因此咱們引出同步異步,阻塞與非阻塞的概念。高併發

同步與異步

同步和異步指的是一個執行流程中每一個方法是否必須依賴前一個方法完成後才能夠繼續執行。假設咱們的執行流程中:依次是方法一和方法二。

同步指的是調用一旦開始,調用者必須等到方法調用返回後,才能繼續後續的行爲。即方法二必定要等到方法一執行完成後才能夠執行。

異步指的是調用馬上返回,調用者沒必要等待方法內的代碼執行結束,就能夠繼續後續的行爲。(具體方法內的代碼交由另外的線程執行完成後,可能會進行回調)。即執行方法一的時候,直接交給其餘線程執行,不禁主線程執行,也就不會阻塞主線程,因此方法二沒必要等到方法一完成便可開始執行。

同步與異步關注的是方法的執行方是主線程仍是其餘線程,主線程的話須要等待方法執行完成,其餘線程的話無需等待馬上返回方法調用,主線程能夠直接執行接下來的代碼。

同步與異步是從多個線程之間的協調來實現效率差別。

爲何須要異步呢?筆者認爲異步的本質就是爲了解決主線程的阻塞,因此網上不少討論把同步異步、阻塞非阻塞進行了四種組合,其中一種就有異步阻塞這一情形,若是異步也是阻塞的?那爲何要特意進行異步操做呢?

阻塞與非阻塞

阻塞與非阻塞指的是單個線程內遇到同步等待時,是否在原地不作任何操做。

阻塞指的是遇到同步等待後,一直在原地等待同步方法處理完成。

非阻塞指的是遇到同步等待,不在原地等待,先去作其餘的操做,隔斷時間再來觀察同步方法是否完成。

阻塞與非阻塞關注的是線程是否在原地等待。

筆者認爲阻塞和非阻塞僅能與同步進行組合。而異步自然就是非阻塞的,而這個非阻塞是對主線程而言。(可能有人認爲異步方法裏面放入阻塞操做的話就是異步阻塞,可是思考一下,正是由於是阻塞操做因此纔會將它放入異步方法中,不要阻塞主線程)

例子講解

海底撈很好吃,可是常常要排隊。咱們就以生活中的這個例子進行講解。

  • A顧客去吃海底撈,就這樣乾坐着等了一小時,而後纔開始吃火鍋。(BIO)

  • B顧客去吃海底撈,他一看要等挺久,因而去逛商場,每次逛一會就跑回來看有沒有排到他。因而他最後既購了物,又吃上海底撈了。(NIO)

  • C顧客去吃海底撈,因爲他是高級會員,因此店長說,你去商場隨便玩吧,等下有位置,我立馬打電話給你。因而C顧客不用幹坐着等,也不用每過一下子就跑回來看有沒有等到,最後也吃上了海底撈(AIO)

哪一種方式更有效率呢?是否是一目瞭然呢?

BIO

BIO全稱是Blocking IO,是JDK1.4以前的傳統IO模型,自己是同步阻塞模式。線程發起IO請求後,一直阻塞IO,直到緩衝區數據就緒後,再進入下一步操做。針對網絡通訊都是一請求一應答的方式,雖然簡化了上層的應用開發,但在性能和可靠性方面存在着巨大瓶頸,試想一下若是每一個請求都須要新建一個線程來專門處理,那麼在高併發的場景下,機器資源很快就會被耗盡。

NIO

NIO也叫Non-Blocking IO 是同步非阻塞的IO模型。線程發起io請求後,當即返回(非阻塞io)。同步指的是必須等待IO緩衝區內的數據就緒,而非阻塞指的是,用戶線程不原地等待IO緩衝區,能夠先作一些其餘操做,可是要定時輪詢檢查IO緩衝區數據是否就緒。

Java中的NIO 是new IO的意思。實際上是NIO加上IO多路複用技術。普通的NIO是線程輪詢查看一個IO緩衝區是否就緒,而Java中的new IO指的是線程輪詢地去查看一堆IO緩衝區中哪些就緒,這是一種IO多路複用的思想。IO多路複用模型中,將檢查IO數據是否就緒的任務,交給系統級別的select或epoll模型,由系統進行監控,減輕用戶線程負擔。

NIO主要有buffer、channel、selector三種技術的整合,經過零拷貝的buffer取得數據,每個客戶端經過channel在selector(多路複用器)上進行註冊。服務端不斷輪詢channel來獲取客戶端的信息。channel上有connect,accept(阻塞)、read(可讀)、write(可寫)四種狀態標識。根據標識來進行後續操做。因此一個服務端可接收無限多的channel。不須要新開一個線程。大大提高了性能。

AIO

AIO是真正意義上的異步非阻塞IO模型。上述NIO實現中,須要用戶線程定時輪詢,去檢查IO緩衝區數據是否就緒,佔用應用程序線程資源,其實輪詢至關於仍是阻塞的,並不是真正解放當前線程,由於它仍是須要去查詢哪些IO就緒。而真正的理想的異步非阻塞IO應該讓內核系統完成,用戶線程只須要告訴內核,當緩衝區就緒後,通知我或者執行我交給你的回調函數。

AIO能夠作到真正的異步的操做,但實現起來比較複雜,支持純異步IO的操做系統很是少,目前也就windows是IOCP技術實現了,而在Linux上,底層仍是是使用的epoll實現的。

筆者我的理解總結,若有錯誤懇請網友評論指正。

推薦好文

>>【練手項目】基於SpringBoot的ERP系統,自帶進銷存+財務+生產功能

>>分享一套基於SpringBoot和Vue的企業級中後臺開源項目,代碼很規範!

>>能掙錢的,開源 SpringBoot 商城系統,功能超全,超漂亮!

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

相關文章
相關標籤/搜索