AIO、BIO和NIO的區別詳解
IO讀寫時,多路複用機制都會依賴一個事件分離器,負責把源事件的IO分離出來,對應到read/write事件分離器。
事件分離器的兩種模式:
1. Reactor:同步IO
2. Proactor:非同步IO(重疊IO)
Reactor
在Reactor模式中,事件分離者等待某個事件或者可應用或個操作的狀態發生(比如檔案描述符可讀寫,或者是socket可讀寫),事件分離者就把這個事件傳給事先註冊的事件處理函式或者回調函式,由後者來做實際的讀寫操作
在Reactor中實現讀:
- 註冊讀就緒事件和相應的事件處理器
- 事件分離器等待事件
- 事件到來,啟用分離器,分離器呼叫事件對應的處理器。
- 事件處理器完成實際的讀操作,處理讀到的資料,註冊新的事件,然後返還控制權。
Proactor
在Proactor模式中,事件處理者(或者代由事件分離者發起)直接發起一個非同步讀寫操作(相當於請求),而實際的工作是由作業系統來完成的。發起時,需要提供的引數包括用於存放讀到資料的快取區,讀的資料大小,或者用於存放外發資料的快取區,以及這個請求完後的回撥函式等資訊。事件分離者得知了這個請求,它默默等待這個請求的完成,然後轉發完成事件給相應的事件處理者或者回調。舉例來說,在Windows上事件處理者投遞了一個非同步IO操作(稱有 overlapped的技術),事件分離者等IOCompletion事件完成,這種非同步模式的典型實現是基於作業系統底層非同步API的,所以我們可稱之為“系統級別”的或者“真正意義上”的非同步,因為具體的讀寫是由作業系統代勞
Proactor(真非同步)中的讀過程:
- 處理器發起非同步讀操作(注意:作業系統必須支援非同步IO)。在這種情況下,處理器無視IO就緒事件,它關注的是完成事件。
- 事件分離器等待操作完成事件
- 在分離器等待過程中,作業系統利用並行的核心執行緒執行實際的讀操作,並將結果資料存入使用者自定義緩衝區,最後通知事件分離器讀操作完成。
- 事件分離器呼喚處理器。
- 事件處理器處理使用者自定義緩衝區中的資料,然後啟動一個新的非同步操作,並將控制權返回事件分離器。
對比
- 兩個模式的相同點,都是對某個IO事件的事件通知(即告訴某個模組,這個IO操作可以進行或已經完成)。在結構上,兩者也有相同點:demultiplexor負責提交IO操作(非同步)、查詢裝置是否可操作(同步),然後當條件滿足時,就回調handler;
- 不同點在於,非同步情況下(Proactor),當回撥handler時,表示IO操作已經完成;同步情況下(Reactor),回撥handler時,表示IO裝置可以進行某個操作(can read or can write),handler這個時候開始提交操作。
AIO、BIO和NIO
BIO,同步阻塞式IO,一個連線一個執行緒
NIO,同步非阻塞IO,一個請求一個執行緒
AIO,非同步非阻塞IO,一個有效請求一個執行緒
BIO
用Java編寫網路請求,都是建立一個ServerSocket,然後,客戶端建立Socket時就會詢問是否有執行緒可以處理,如果沒有,要麼等待,要麼被拒絕。即:一個連線,要求Server對應一個處理執行緒。
NIO
NIO由三個主要的部分組成:緩衝區(Buffers)、通道(Channels)和非阻塞I/O的核心類組成。
NIO本身是基於事件驅動思想來完成的,其主要想解決的是BIO的高併發問題: 在使用同步I/O的網路應用中,如果要同時處理多個客戶端請求,或是在客戶端要同時和多個伺服器進行通訊,就必須使用多執行緒來處理。也就是說,將每一個客戶端請求分配給一個執行緒來單獨處理。這樣做雖然可以達到我們的要求,但同時又會帶來另外一個問題。由於每建立一個執行緒,就要為這個執行緒分配一定的記憶體空間(也叫工作儲存器),而且作業系統本身也對執行緒的總數有一定的限制。如果客戶端的請求過多,服務端程式可能會因為不堪重負而拒絕客戶端的請求,甚至伺服器可能會因此而癱瘓。
NIO基於Reactor,當socket有流可讀或可寫入socket時,作業系統會相應的通知引用程式進行處理,應用再將流讀取到緩衝區或寫入作業系統。
也就是說,這個時候,已經不是一個連線就要對應一個處理執行緒了,而是有效的請求,對應一個執行緒,當連線沒有資料時,是沒有工作執行緒來處理的。
最基本的兩個概念:
Buffer:
– 是一塊連續的記憶體塊。
– 是 NIO 資料讀或寫的中轉地。
Channel:
– 資料的源頭或者資料的目的地
– 用於向 buffer 提供資料或者讀取 buffer 資料 ,buffer 物件的唯一介面。
– 非同步 I/O 支援
AIO
AIO基礎Proactor模式,它與NIO不同,當進行讀寫操作時,只須直接呼叫API的read或write方法即可。這兩種方法均為非同步的,對於讀操作而言:
1. 當有流可讀取時,作業系統會將可讀的流傳入read方法的緩衝區,並通知應用程式
2. 對於寫操作而言,當作業系統將write方法傳遞的流寫入完畢時,作業系統主動通知應用程式。
同步和非同步區別
按照《Unix網路程式設計》的劃分,IO模型可以分為:阻塞IO、非阻塞IO、IO複用、訊號驅動IO和非同步IO,按照POSIX標準來劃分只分為兩類:同步IO和非同步IO。如何區分呢?首先一個IO操作其實分成了兩個步驟:
1. 發起IO請求
2. 實際的IO操作
同步IO和非同步IO的區別就在於第二個步驟是否阻塞,如果實際的IO讀寫阻塞請求程序,那麼就是同步IO,因此阻塞IO、非阻塞IO、IO複用、訊號驅動IO都是同步IO,如果不阻塞,而是作業系統幫你做完IO操作再將結果返回給你,那麼就是非同步IO。阻塞IO和非阻塞IO的區別在於第一步,發起IO請求是否會被阻塞,如果阻塞直到完成那麼就是傳統的阻塞IO,如果不阻塞,那麼就是非阻塞IO。