3C資訊

Java I/O模型及其底層原理

  Java I/O是Java基礎之一,在面試中也比較常見,在這裏我們嘗試通過這篇文章闡述Java I/O的基礎概念,幫助大家更好的理解Java I/O。
  在剛開始學習Java I/O時,我很迷惑,因為網上絕大多數的文章都是講解Linux網絡I/O模型的,那時我總是搞不明白和Java I/O的關係。後來查了看了好多,才明白Java I/O的原理是以Linux網絡I/O模型為基礎的,理解了Linux網絡I/O模型再學習Java I/O就很方便了,所以這篇文章,我們先來了解I/O的基本概念,再學習Linux網絡I/O模型,最後再看Java中的幾種I/O。

什麼是I/O?

  I/O是Input、Output的縮寫,即對應計算機中的輸入輸出,以一次文件讀取為例,我們需要將磁盤上的數據讀取到用戶空間,那麼這次數據轉移操作其實就是一次I/O操作,更具體的說是一次文件I/O。我們瀏覽網頁,其中在請求一個網頁時,服務器通過網絡把數據發送給我們,此時程序將數據從TCP緩衝區複製到用戶空間,那麼這次數據轉移操作其實也是一次I/O操作,更具體的說是一次網絡I/O。I/O到處都在,十分重要,Java對I/O對底層操作系統的各種I/O模型進行了封裝,使我們可以輕鬆開發。

Linux網絡I/O模型

  根據UNIX網絡編程對I/O模型的分類,UNIX提供了5種I/O模型,分別是:阻塞I/O(Blocking I/O)、非阻塞I/O(Non-Blacking I/O)、I/O多路復用模型(I/O Multiplexing)、信號驅動式I/O(Signal Driven I/O)、異步I/O(Asynchronous I/O)。我們逐步了解一下其基本原理。

阻塞I/O(Blocking I/O)

  阻塞I/O是最早最基礎的I/O模型,其在讀寫數據過程中會阻塞。通過下圖我們可以看到,當用戶進程調用了recvfrom這個系統調用后,內核開始第一階段的數據準備工作,直到內核等待數據準備完成,然後開始第二階段的將數據從內核複製到用戶空間的工作,最後內核返回結果。整個過程中用戶進程都是阻塞的,直到最後返回結果后才接觸阻塞block狀態。阻塞I/O模型適用於併發量小且對時延不敏感的系統。

非阻塞I/O(Non-Blacking I/O)

  當用戶進程調用recvfrom這個系統調用后,如果內核尚未準備好數據,此時不再阻塞用戶進程,而是立即返回一個EWOULDBLOCK錯誤。用戶進程會不斷髮起系統調用直到內核中數據被準備好(輪詢),此時將執行第二階段的將數據從內核複製到用戶空間的工作,然後內核返回結果。非阻塞I/O模型不斷地輪詢往往需要耗費大量cpu時間。

I/O多路復用模型(I/O Multiplexing)

  I/O多路復用的優點在於單個進程可以同時處理多個網絡連接的I/O,其基本原理就是select/epoll函數可以不斷的輪詢其負責的所有socket,當某個socket有數據到達時,就通知用戶進程。
  如下圖所示,當用戶進程調用select函數時,整個進程會被阻塞block住,但是這裏的阻塞不是被socket I/O阻塞,而是被select這個函數阻塞。同時內核會監聽改select負責的所有socket(這裏的socket一般設置為non-blocking),當任何一個socket中的數據準備好時,select就會返回給用戶進程,這時候用戶進程再此發起一個系統調用,將數據從內核複製到用戶空間,並返回結果。
  對比I/O多路復用模型和阻塞I/O模型的流程,多路復用多了一個系統調用來完成select環節,除此之外沒有太大的不同。Select的優勢在於它可以同時處理多個connection,但是會多一個系統調用。多路復用本質上也不是非阻塞的。

信號驅動式I/O(Signal Driven I/O)

  首先我們開啟socket的信號驅動I/O功能,然後用戶進程發起sigaction系統調用給內核后立即返回並可繼續處理其他工作。收到sigaction系統調用的內核在將數據準備好後會按照要求產生一個signo信號通知給用戶進程。然後用戶進程再發起recvfrom系統調用,完成數據從內核到用戶空間的複製,並返回最終結果。其基礎原理圖示如下:

異步I/O(Asynchronous I/O)

  用戶進程向內核發起系統調用后,就可以開始去做其他事情了。內核收到異步I/O的系統調用后,會直接retrun,所以這裏不會對用戶進程有阻塞。之後內核等待數據準備完成後會繼續將數據從內核拷貝到用戶空間(具體動作可以由異步I/O調用定義),然後內核回給用戶進程發送一個signal,告訴用戶進程I/O操作完成了,整個過程不會導致用戶請求進程阻塞。
  信號驅動I/O模型是內核通知我們可以發起I/O操作了,而異步I/O模式是內核告訴我們I/O操作已經完成了。

  以上就是Linux的5種網絡I/O模型,其中前4中都是同步I/O模型,他們真正的I/O操作環節都會將進程阻塞,只有最後一種異步I/O模型是異步I/O操作。

Java中的I/O模型

  在JDK1.4之前,基於Java的所有socket通信都是使用阻塞I/O(BIO),JDK1.4提供了了非阻塞I/O(NIO)功能,不過雖然名字叫做NIO,實際底層模型是I/O多路復用,JDK1.7提供了針對異步I/O(AIO)功能。

BIO

  BIO簡化了上層開發,但是性能瓶頸問題嚴重,對高併發第時延支持差。
基於消息隊列和線程池技術優化的BIO模式雖然可以對高併發支持有一定幫助,但是還是受限於線程池大小和線程池阻塞隊列大小的制約,當併發數超過線程池的處理能力時,部分請求法務繼續處理,會導致客戶端連接超時,影響用戶體驗。

NIO

  NIO彌補了BIO的不足,簡單說就是通過selector不斷輪詢註冊在自己上面的channel,如果channel上面有新的連接讀寫時間時就會被輪詢出來,一個selector上面可以註冊多個channel,一個線程就可以負責selector的輪詢,這樣就可以支持成千上萬的連接。Selector就是一個輪詢器,channel是一個通道,通過它來讀取或者寫入數據,通道是雙向的,可以用於讀、寫、讀和寫。Buffer用來和channel交互,數據通過channel進出buffer。
NIO的優點是可以可靠性好以及高併發低時延,但是使用NIO的代碼開發較為複雜。

AIO

  AIO,或者說叫做NIO2.0,引入了異步channel的概念,提供了異步文件channel和異步socket channel的實現,開發者可以通過Future類來表示異步操作的結果,也可以在執行異步操作時傳入一個channels,實現CompletionHandler接口作為回調。AIO不用開發者單獨開發獨立線程的selector,異步回調操作有JDK地城思安城池負責驅動,開發起來比NIO簡單一些,同時保持了高可靠高併發低時延的優點。

參考:
https://blog.csdn.net/historyasamirror/article/details/5778378
https://juejin.im/post/5cce5019e51d453a506b0ebf

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?