聊聊Java BIO(同步阻塞IO)、NIO(非阻塞IO)、AIO(異步IO)

Java中有阻塞IO、非阻塞IO。阻塞IO可以理解爲“一個連接對應於一線程”。非阻塞IO可以理解爲“一個請求(一個請求裏面可能會有多個連接【長連接短連接】)對應於一線程”。

BIO

Java中BIO也成爲同步阻塞IO。

同步阻塞IO模式下,服務器實現模式爲一個連接對應一個線程,即:有連接請求從客戶端發起時,服務器端就需要創建一個線程進行處理,如果有大量連接時,服務器就需要創建大量線程進行處理。當然可以通過線程池機制改善。

阻塞IO適用場景爲:連接數較小且固定的架構模式,這種方式對服務器資源要求比較高,併發侷限於應用中,不建議在生產環境使用。

NIO(非阻塞IO)

Java中非阻塞式IO,也叫同步非阻塞IO,其通過通道和緩衝區來讀寫數據。通過選擇器註冊和獲取已準備好的感興趣的通道事件。讀數據時,有多少數據就讀多少數據,讀完立即返回,如SocketChannel和ServerSocketChannel、Selector等。

NIO本身是基於事件驅動思想來完成的,其主要想解決的是BIO的大併發問題。NIO基於Reactor,當socket有流可讀或可寫入socket時,操作系統會相應的通知引用程序進行處理,應用再將流讀取到緩衝區或寫入操作系統。

也就是說,這個時候,已經不是一個連接就要對應一個處理線程了,而是有效的請求,對應一個線程,當連接沒有數據時,是沒有工作線程來處理的。

NIO的最重要的地方是,當一個連接創建後,不需要對應一個線程,這個連接會被註冊到多路複用器上面。所以,所有的連接只需要一個線程就可以搞定,當這個線程中的多路複用器進行輪詢的時候,發現連接上有請求的話,纔開啓一個線程進行處理,也就是一個請求一個線程模式。

在NIO的處理方式中,當一個請求來的話,開啓線程進行處理,可能會等待後端應用的資源(JDBC連接等),其實這個線程就被阻塞了,當並發上來的話,還是會有BIO一樣的問題。

Java NIO 由以下幾個核心部分組成:

  • Channels
  • Buffers
  • Selectors

雖然Java NIO 中除此之外還有很多類和組件,但是,Channel,Buffer和Selector構成了核心的API。其它組件,如Pipe和FileLock,只不過是與三個核心組件共同使用的工具類。

Channel vs Buffer

Channel和Buffer:基本上,所有的IO在NIO中都從一個Channel開始。Channel有點像流,數據可以從Channel讀到Buffer中,也可以從Buffer寫到Channel中。如:Channel和Buffer有好幾種類型。下面是JAVA NIO中的一些主要Channel的實現:FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel。這些通道涵蓋了UDP 和 TCP 網絡IO,以及文件IO。

在這裏插入圖片描述
Java NIO裏關鍵的Buffer實現:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer、MappedByteBuffer(內存映射文件 )。這些Buffer覆蓋了能通過IO發送的基本數據類型:byte,short,int,long,float,double和char。

Channel和Buffer使用案例:改代碼以隨機讀寫方式打開nio-data.txt文件,一個字節一個字節的把文件內容讀入新分配的buf緩存中。

在這裏插入圖片描述

Selector

Selector允許單線程處理多個 Channel。如果應用打開了多個連接(通道),但每個連接的流量都很低,使用Selector就會很方便。例如,在一個聊天服務器中。單線程中使用一個Selector處理3個Channel的圖示:

在這裏插入圖片描述
要使用Selector,得向Selector註冊Channel,然後調用它的select()方法。這個方法會一直阻塞到某個註冊的通道有事件就緒。一旦這個方法返回,線程就可以處理這些事件,事件的例子有如新連接進來,數據接收等。

Selector使用案例:該案例使用Selector已訂閱事件的通道進行選擇,當訂閱事件發生時即可另行開闢線程處理該通道事件。
在這裏插入圖片描述

AIO(異步IO)

Java中的異步IO,通過Future輪詢和Callback回調兩種方式來使用。

與NIO不同,當進行讀寫操作時,異步IO情況下,我們只需要直接調用API的read或write方法即可。這兩個操作都是異步的。

對於讀操作而言,當有流可讀時,操作系統會將可讀的流傳入read方法的緩衝區,並通知應用程序;
對於寫操作而言,當操作系統將write方法傳遞的流寫入完畢時,操作系統主動通知應用程序。
即可以理解爲,read/write方法都是異步的,完成後會主動調用回調函數。在JDK1.7中,這部分內容被稱作NIO.2,主要在java.nio.channels包下增加了下面四個異步通道:AsynchronousSocketChannel、AsynchronousServerSocketChannel、AsynchronousFileChannel、AsynchronousDatagramChannel。

Future輪詢模式

典型的Java通道類有AsynchronousFileChannel。來看一個例子:
在這裏插入圖片描述
該案例通過異步IO通道AsynchronousFileChannel將文件foobar.txt的內容讀入buffer中,在通過Future獲取結果。Java將讀取文件的操作交給操作系統底層去做,自己只需要知道文件是否讀取完畢就好了。其實底層JVM爲執行這個任務創建了線程池和通道組。具體可以參考AsynchronousFileChannel的官方說明:

An AsynchronousFileChannel is associated with a thread pool to which tasks are submitted to handle I/O events and dispatch to completion handlers that consume the results of I/O operations on the channel. The completion handler for an I/O operation initiated on a channel is guaranteed to be invoked by one of the threads in the thread pool (This ensures that the completion handler is run by a thread with the expected identity). Where an I/O operation completes immediately, and the initiating thread is itself a thread in the thread pool, then the completion handler may be invoked directly by the initiating thread. When an AsynchronousFileChannel is created without specifying a thread pool then the channel is associated with a system-dependent default thread pool that may be shared with other channels. The default thread pool is configured by the system properties defined by the AsynchronousChannelGroup class.

Callback回調模式

Future其實本質上還是輪循的方式,回調式纔是真正的AIO。其基本思想是主線程註冊一個CompletionHanlder執行IO操作。CompletionHanlde將帶着IO操作的結果返回到主線程中,這個結果會觸發它自己的completed或者failed方法(需要重寫這兩個方法):

  1. void completed(V result, A attachment) - executes if a task completes with a result of type V.
  2. void failed(Throwable e, A attachment) - executes if the task fails to complete due to Throwable e.

callback回調案例:
在這裏插入圖片描述
上面的例子是基於文件的AsynchronousFileChannel,基於網絡套接字的AsynchronousServerSocketChannel和AsynchronousSocketChannel也是一樣的模式。

喜歡的可以關注微信訂閱號:

在這裏插入圖片描述

或者可以關注頭條公衆號:

  1. 程序員架構
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章