1. 程式人生 > >高併發程式設計系列:NIO、BIO、AIO的區別,及NIO的應用和框架選型

高併發程式設計系列:NIO、BIO、AIO的區別,及NIO的應用和框架選型

文章目錄


談到併發程式設計就不得不提到NIO,以及相關的Java NIO框架Netty等,並且在很多面試中也經常提到NIO和AIO、同步和非同步、阻塞和非阻塞等的區別。我先簡短介紹下幾個NIO相關的概念,然後再談NIO重點掌握內容,以及Java NIO框架選型。

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操作時,由於執行緒會阻塞,所以伺服器端必須為每個客戶端都提供一個獨立的執行緒進行處理,當伺服器端需要處理大量的客戶端時,效能急劇下降。

非阻塞:Java
NIO是非阻塞式的。當執行緒從某通道進行讀寫資料時,若沒有資料可用時,該執行緒會去執行其他任務。執行緒通常將非阻塞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

socketChannel.register(selector, SelectionKey.OP_READ);

監聽的事件有

  • 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的實現原理以及實戰場景。

轉載原文:
http://youzhixueyuan.com/java-nio-introduce.html