BIO、NIO、AIO,還傻傻分不清?

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

咱們知道java的I/O模型一共有四種,分別是:傳統的BIO,僞異步I/O,NIO和AIO。前端

爲了澄清概念和分清區別,咱們仍是先簡單的介紹一下他們的概念,而後再去比較優劣。java

1.概念澄清

1.1 BIO

BIO,即Blocking I/O。面試

網絡編程的基本模型是Client/Server 模型,也就是兩個進程之間進行相互通訊,其中服務端提供位置信息(綁定的Ip 地址和監聽端口) ,客戶端經過鏈接操做向服務端監聽的地址發起鏈接請求,經過三次握手創建鏈接,若是鏈接建在成功,雙方就能夠經過網絡套接字( Socket ) 進行通訊。編程

在基於傳統同步阻塞模型開發中, ServerSocket 負責綁定IP 地址,啓動監聽端口:Socket 負責發起鏈接操做。鏈接成功以後,雙方經過輸入和輸出流進行同步阻塞式通訊。後端

BIO通訊模型圖:服務器

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

解釋一下上圖:微信

採用BIO通訊模型的服務端,一般由一個獨立的Acceptor線程負責監聽客戶端的鏈接,它接收到客戶端鏈接請求以後爲每一個客戶端建立一個新的線程進行鏈路處理,處理完成以後,經過輸出流返回應答給客戶端, 統程銷燬。這就是典型的一請求一回答通訊模型。網絡

對於這種IO模型咱們知道:用戶線程發出IO請求以後,內核會去查看數據是否就緒,若是沒有就緒就會等待數據就緒,而用戶線程就會處於阻塞狀態,用戶線程交出CPU。當數據就緒以後,內核會將數據拷貝到用戶線程,並返回結果給用戶線程,用戶線程才解除block狀態。即在讀寫數據過程當中會發生阻塞現象。併發

1.2 僞異步IO

爲了解決同步阻塞 I/O 面臨的一個鏈路須要一個線程處理的問題,後來有人對它的線程模型進行了優化一一後端經過一個線程池來處理多個客戶端的請求接入,造成客戶端個數M: 線程池最大線程數N 的比例關係,其中M 能夠遠遠大於N。經過線程地能夠靈活地調配線程資源,設置線程的最大值,防止因爲海量併發接入致使線程耗盡。框架

僞異步IO通訊模型圖:

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

採用線程池和任務隊列能夠實現僞異步I/O通訊框架。當有新的客戶端接入時,將客戶端的Socket 封裝成一個Task (該任務實現java.lang. Runnable 接口)投遞到後端的線程池中進行處理, JDK 的線程將維護一個消息隊列和N個活躍線程, 對消息隊列中的任務進行處理。

因爲統程池能夠設置消息隊列的大小和最大線程數,所以,它的資源佔用是可控的, 不管多少個客戶端併發訪問, 都不會致使資源的耗盡和省機。

僞異步I/O 通訊框架採用了線程池實現,所以避免了爲每一個請求都建立一個獨立線程形成的線程資源耗盡問題。可是因爲它底層的通訊依然採用同步阻塞模型,所以沒法從根本上解決問題。

僞異步I/O 實際上僅僅是對以前I/O 線程模型的一個簡單優化,它沒法從根本上解決同步I/O 致使的通訊線程阻塞問題。下面咱們就簡單分析下通訊對方返回應答時間過長會引發的級聯故障。

  1. 服務端處理緩慢,返回應答消息耗費60s,平時只須要10ms;
  2. 採用僞異步I/O 的線程在讀取故障服務節點的響應,因爲讀/取輸入流是阻塞的,它將會被同步阻塞60s;
  3. 假如全部的可用線程都被故障服務器阻塞,那後續的全部的I/O消息都將在隊列中排隊;
  4. 因爲線程地採用阻塞隊列實現,當隊列積滿以後,後續入隊列的操做將被阻塞;
  5. 因爲前端只有一個Accptor 線程接收客戶端接入,它被阻塞在線程池的同步阻塞隊列以後,新的客戶端請求消息將被拒絕,客戶端會發生大量的鏈接超時;
  6. 因爲幾於全部的鏈接都超時,調用者會認爲系統已經崩潰,沒法接收新的請求消息。

如何破解這個難題?下面咱們再看一下NIO。微信搜索Java技術棧,在後臺回覆:面試,能夠獲取我整理的 Java/ IO 系列面試題和答案,很是齊全。

1.3 NIO

NIO,不少人叫他New I/O,因爲以前老的I/O 類庫是阻塞I/O ,New I/O 類庫的目標就是要讓Java 支持非阻塞I/O,因此,更多的人喜歡稱之爲非阻塞I/O(Non-block I/O)。

與Socket類和ServerSocket 類相對應, NIO也提供了SocketChannel 和ServerSocketChannel兩種不一樣的套接字通道實現。這兩種新增的通道都支持阻塞和非阻塞兩種模式。阻塞模式使用很是簡單,可是性能和可靠性都很差,非阻塞模式則正好相反。開發人員能夠根據本身的須要來選擇合適的模式。

通常來講,低負載、低併發的應用程序能夠選擇同步阻塞I/O以下降編程複雜度:對於高負載、高併發的網絡應用,須要使用NIO 的非阻塞模式進行開發。

前面咱們已經對NIO進行了介紹,咱們知道NIO中引入了緩衝區Buffer,通道Channel和多路複用器Selector的概念。一個多路複用器Selector 能夠同時輪詢多個Channel,而Channel又是全雙工的,同時支持讀寫操做,使用NIO 編程的優勢總結以下:

  1. 客戶端發起的鏈接操做是異步的,能夠經過在多路複用器註冊OP_CONNECT 等待後續結果,不須要像以前的客戶端那樣被同步阻塞。
  2. SocketChannel 的讀寫操做都是異步的,若是沒有可讀寫的數據它不會同步等待,直接返回,這樣I/O 通訊線程就能夠處理其餘的鏈路,不須要同步等待這個鏈路可用。
  3. 線程模型的優化:因爲JDK 的Selector 在Linux 等主流操做系統上經過epoll 實現,它沒有鏈接句柄數的限制(只受限於操做系統的最大句柄數或者對單個進程的句柄限制),這意味着一個Selector 線程能夠同時處理成千上萬個客戶端鏈接,並且性能不會隨着客戶端的增長而線性降低。所以,它很是適合作高性能、高負載的網絡服務器。
1.4 AIO

NIO 2.0 引入了新的異步通道的概念,並提供了異步文件通道和異步套接字通道的實現。

異步通道提供如下兩種方式獲取獲取操做結果:

▷經過java.util.concurrent.Future 類來表示異步操做的結果;

▷在執行異步操做的時候傳入一個java.nio.channels;

NIO 2.0 的異步套接字通道是真正的異步非阻塞I/O ,對應於UNIX 網絡編程中的事件 驅動I/O (AIO) 。它不須要經過多路複用器( Selector) 對註冊的通道進行輪詢操做便可實 現異步讀寫,從而簡化了NIO 的編程模型。

前面對不一樣的I/O模型進行了簡單介紹,不一樣的I/O 模型因爲線程模型、API 等差異很大,因此用法的差別也很是大。

咱們用一個表格來作一個統一說明:

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

-END-

 

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

相關文章
相關標籤/搜索