AIO、BIO、NIO的區別
IO模型主要分類:
- 同步(synchronous) IO和異步(asynchronous) IO
- 阻塞(blocking) IO和非阻塞(non-blocking)IO
- 同步阻塞(blocking-IO)簡稱BIO
- 同步非阻塞(non-blocking-IO)簡稱NIO
- 異步非阻塞(synchronous-non-blocking-IO)簡稱AIO
1.BIO (同步阻塞I/O模式)
數據的讀取寫入必須阻塞在一個線程內等待其完成。
這裏使用那個經典的燒開水例子,這裏假設一個燒開水的場景,有一排水壺在燒開水,BIO的工作模式就是, 叫一個線程停留在一個水壺那,直到這個水壺燒開,纔去處理下一個水壺。但是實際上線程在等待水壺燒開的時間段什麼都沒有做。
2.NIO(同步非阻塞)
同時支持阻塞與非阻塞模式,但這裏我們以其同步非阻塞I/O模式來說明,那麼什麼叫做同步非阻塞?如果還拿燒開水來說,NIO的做法是叫一個線程不斷的輪詢每個水壺的狀態,看看是否有水壺的狀態發生了改變,從而進行下一步的操作。
3.AIO (異步非阻塞I/O模型)
異步非阻塞與同步非阻塞的區別在哪裏?異步非阻塞無需一個線程去輪詢所有IO操作的狀態改變,在相應的狀態改變後,系統會通知對應的線程來處理。對應到燒開水中就是,爲每個水壺上面裝了一個開關,水燒開之後,水壺會自動通知我水燒開了。
4.IO與NIO區別:
5.同步與異步的區別:
同步:發送一個請求,等待返回,再發送下一個請求,同步可以避免出現死鎖,髒讀的發生。
異步:發送一個請求,不等待返回,隨時可以再發送下一個請求,可以提高效率,保證併發。
6.阻塞和非阻塞
阻塞:傳統的IO流都是阻塞式的。也就是說,當一個線程調用read()或者write()方法時,該線程將被阻塞,直到有一些數據讀讀取或者被寫入,在此期間,該線程不能執行其他任何任務。在完成網絡通信進行IO操作時,由於線程會阻塞,所以服務器端必須爲每個客戶端都提供一個獨立的線程進行處理,當服務器端需要處理大量的客戶端時,性能急劇下降。
非阻塞:JavaNIO是非阻塞式的。當線程從某通道進行讀寫數據時,若沒有數據可用時,該線程會去執行其他任務。線程通常將非阻塞IO的空閒時間用於在其他通道上執行IO操作,所以單獨的線程可以管理多個輸入和輸出通道。因此NIO可以讓服務器端使用一個或有限幾個線程來同時處理連接到服務器端的所有客戶端。
7.BIO、NIO、AIO適用場景
- BIO方式適用於連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇。
- NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,併發侷限於應用中,編程比較複雜。
- AIO方式使用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與併發操作,編程比較複雜,JDK7開始支持。
NIO的3個核心概念
NIO重點是把Channel(通道),Buffer(緩衝區),Selector(選擇器)三個類之間的關係弄清楚。
1.緩衝區Buffer
Buffer是一個對象。它包含一些要寫入或者讀出的數據。在面向流的I/O中,可以將數據寫入或者將數據直接讀到Stream對象中。
在NIO中,所有的數據都是用緩衝區處理。這也就本文上面談到的IO是面向流的,NIO是面向緩衝區的。
緩衝區實質是一個數組,通常它是一個字節數組(ByteBuffer),也可以使用其他類的數組。但是一個緩衝區不僅僅是一個數組,緩衝區提供了對數據的結構化訪問以及維護讀寫位置(limit)等信息。
最常用的緩衝區是ByteBuffer,一個ByteBuffer提供了一組功能於操作byte數組。除了ByteBuffer,還有其他的一些緩衝區,事實上,每一種Java基本類型(除了Boolean)都對應一種緩衝區,具體如下:
- ByteBuffer:字節緩衝區
- CharBuffer:字符緩衝區
- ShortBuffer:短整型緩衝區
- IntBuffer:整型緩衝區
- LongBuffer:長整型緩衝區
- FloatBuffer:浮點型緩衝區
- DoubleBuffer:雙精度浮點型緩衝區
2.通道Channel
Channel是一個通道,可以通過它讀取和寫入數據,他就像自來水管一樣,網絡數據通過Channel讀取和寫入。
通道和流不同之處在於通道是雙向的,流只是在一個方向移動,而且通道可以用於讀,寫或者同時用於讀寫。
因爲Channel是全雙工的,所以它比流更好地映射底層操作系統的API,特別是在UNIX網絡編程中,底層操作系統的通道都是全雙工的,同時支持讀和寫。
Channel有四種實現:
- FileChannel:是從文件中讀取數據。
- DatagramChannel:從UDP網絡中讀取或者寫入數據。
- SocketChannel:從TCP網絡中讀取或者寫入數據。
- ServerSocketChannel:允許你監聽來自TCP的連接,就像服務器一樣。每一個連接都會有一個SocketChannel產生。
3.多路複用器Selector
Selector選擇器可以監聽多個Channel通道感興趣的事情(read、write、accept(服務端接收)、connect,實現一個線程管理多個Channel,節省線程切換上下文的資源消耗。Selector只能管理非阻塞的通道,FileChannel是阻塞的,無法管理。
關鍵對象
- Selector:選擇器對象,通道註冊、通道監聽對象和Selector相關。
- SelectorKey:通道監聽關鍵字,通過它來監聽通道狀態。
監聽註冊
監聽註冊在Selector
監聽的事件有
- OP_ACCEPT: 接收就緒,serviceSocketChannel使用的
- OP_READ: 讀取就緒,socketChannel使用
- OP_WRITE: 寫入就緒,socketChannel使用
- OP_CONNECT: 連接就緒,socketChannel使用
NIO的應用和框架
1.NIO的應用
Java NIO成功的應用在了各種分佈式、即時通信和中間件Java系統中,充分的證明了基於NIO構建的通信基礎,是一種高效,且擴展性很強的通信架構。
例如:Dubbo(服務框架),就默認使用Netty作爲基礎通信組件,用於實現各進程節點之間的內部通信。
Jetty、Mina、Netty、Dubbo、ZooKeeper等都是基於NIO方式實現。
- Mina出身於開源界的大牛Apache組織
- Netty出身於商業開源大亨Jboss
- Dubbo阿里分佈式服務框架
2.NIO框架
特別是Netty是目前最流行的一個Java開源框架NIO框架,Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。
相比JDK原生NIO,Netty提供了相對十分簡單易用的API,非常適合網絡編程。
Mina和Netty這兩個NIO框架的創作者是同一個人Trustin Lee 。Netty從某種程度上講是Mina的延伸和擴展,解決了一些Mina上的設計缺陷,也優化了一下Mina上面的設計理念。
另一方面Netty相比較Mina的優勢:
- 更容易學習
- API更簡單
- 詳細的範例源碼和API文檔
- 更活躍的論壇和社區
- 更高的代碼更新維護速度
Netty無疑是NIO框架的首選,它的健壯性、功能、性能、可定製性和可擴展性在同類框架都是首屈一指的,後續將重點詳細談Netty的實現原理以及實戰場景。