聊聊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 BIO(同步阻塞IO)、NIO(非阻塞IO)、AIO(非同步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快取中。

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

Selector

Selector允許單執行緒處理多個 Channel。如果應用打開了多個

連線

(通道),但每個連線的流量都很低,使用Selector就會很方便。例如,在一個聊天伺服器中。單執行緒中使用一個Selector處理3個Channel的圖示:

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

要使用Selector,得向Selector註冊Channel,然後呼叫它的select()方法。

這個方法會一直阻塞

到某個註冊的通道有事件就緒。一旦這個方法返回,執行緒就可以處理這些事件,事件的例子有如新連線進來,資料接收等。

Selector使用案例:該案例使用Selector已訂閱事件的通道進行選擇,當訂閱事件發生時即可另行開闢執行緒處理該通道事件。

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

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。來看一個例子:

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

該案例透過非同步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方法(需要重寫這兩個方法):

void completed(V result, A attachment) - executes if a task completes with a result of type V。

void failed(Throwable e, A attachment) - executes if the task fails to complete due to Throwable e。

callback回撥案例:

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

上面的例子是基於檔案的AsynchronousFileChannel,但是基於網路套接字的AsynchronousServerSocketChannel和AsynchronousSocketChannel也是一樣的模式。