Java NIO AIO 基本概念

一、NIO

在介紹NIO編程之前,我們首先需要澄清一個概念:NIO到底是什麼的簡稱?有人稱之爲New I/O,因爲它相對於之前的I/O類庫是新增的,所以被稱爲New I/O,這是它的官方叫法。但是,由於之前老的I/O類庫是阻塞I/O,New I/O類庫的目標就是要讓Java支持非阻塞I/O,所以,更多的人喜歡稱之爲非阻塞I/O(Non-block I/O),由於非阻塞I/O更能夠體現NIO的特點,所以我們這裏使用NIO表示非阻塞I/O。

與Socket類和ServerSocket類相對應,NIO也提供了SocketChannel和ServerSocketChannel兩種不同的套接字通道實現。這兩種新增的通道都支持阻塞和非阻塞兩種模式。阻塞模式使用非常簡單,但是性能和可靠性都不好,非阻塞模式則正好相反。開發人員一般可以根據自己的需要來選擇合適的模式,一般來說,低負載,低併發的應用程序可以選擇同步阻塞I/O以降低編程複雜度,但是對於高負載,高併發的網絡應用,需要使用NIO的非阻塞模式進行開發。

NIO類庫簡介

1、緩衝區Buffer

我們首先介紹緩衝區(Buffer)的概念,Buffer是一個對象,它包含一些要寫入或者要讀出的數據。在NIO類庫中加入Buffer對象,體現了新庫與原I/O的一個重要區別。在面向流的I/O中,可以將數據直接寫入或者將數據直接讀到Stream對象中。

在NIO庫中,所有數據都是用緩衝區處理的。在讀取數據時,它是直接讀到緩衝區中的;在寫入數據時,寫入到緩衝區中。任何時候訪問NIO中的數據,都是通過緩衝區進行操作。

緩衝區實質上是一個數組。通常它是一個字節數組(ByteBuffer),也可以使用其他種類的數組。但是一個緩衝區不僅僅是一個數組,緩衝區提供了對數據的結構化訪問以及維護讀寫位置(limit)等信息。

最常用的緩衝區是ByteBuffer,一個ByteBuffer提供了一組功能用於操作byte數組。除了ByteBuffer,還有其他的一些緩衝區,事實上,每一種Java基本類型(除了Boolean類型)都對應有一種緩衝區,具體如下:

  • ByteBuffer:字節緩衝區
  • CharBuffer:字符緩衝區
  • ShortBuffer:短整型緩衝區
  • IntBuffer:整型緩衝區
  • LongBuffer:長整型緩衝區
  • FloatBuffer:浮點型緩衝區
  • DoubleBuffer:雙精度浮點型緩衝區

緩衝區的繼承關係如下圖所示:
在這裏插入圖片描述
每一個Buffer類都是Buffer接口的一個子實例。除了ByteBuffer,每一個Buffer類都有完全一樣的操作,只是它們所處理的數據類型不一樣。因爲大多數標準I/O操作都使用ByteBuffer,所以它除了具有一般緩衝區的操作之外還提供一些特有的操作,方便網絡讀寫。

2、通道Channel

Channel是一個通道,可以通過它讀取和寫入數據,它就像輸水管道一樣,網絡數據通過Channel讀取和寫入。通道與流的不同之處在於通道是雙向的,流只是在一個方向上移動(一個流必須是InputStream 或者 OutputStream的子類),而且通道可以用於讀,寫或者同時用於讀寫。

因爲Channel是全雙工的,所以它可以比流更好地映射底層操作系統的API。特別是在UNIX網絡編程模型中,底層操作系統的通道都是全雙工的,同時支持讀寫操作。

Channel的集成關係如下圖所示:
在這裏插入圖片描述

自頂向下看, 前三層主要是Channel接口,用於定義它的功能,後面是一些具體的功能類(抽象類)。從類圖可以看出,實際上Channel可以分爲兩大類:分別是用於網絡讀寫的SelectableChannel和用於文件操作的FileChannel。

3、多路複用器Selector

多路複用器Selector,它是Java NIO編程的基礎,熟練地掌握Selector對於掌握NIO編程至關重要。多路複用器提供選擇已經就緒的任務的能力。簡單來講, Selector會不斷地輪詢註冊在其上的Channel,如果某個Channel上面有新的TCP連接接入,讀和寫事件,這個Channel就處於就緒狀態,會被Selector輪詢出來,然後通過SelectionKey可以獲取就緒Channel的集合,進行後續的I/O操作 。

一個多路複用器Selector可以同時輪詢多個Channel,由於JDK使用了epoll()代替傳統的select實現,所以它並沒有最大連接句柄1024/2048的限制。這也就意味着只需要一個線程負責Selector的輪詢,就可以接入成千上萬的客戶端,這確實是個非常巨大的進步。

4、NIO服務端序列圖

在這裏插入圖片描述
儘管NIO編程難度確實比同步阻塞BIO大很多,但是我們要考慮到它的優點:

  1. 客戶端發起的連接操作是異步的,可以通過在多路複用器註冊OP_CONNECT等後續結果,不需要像之前的客戶端那樣被同步阻塞。
  2. SocketChannel的讀寫操作都是異步的,如果沒有可讀寫的數據它不會同步等待,直接返回,這樣IO通信線程就可以處理其它的鏈路,不需要同步等待這個鏈路可用。
  3. 線程模型的優化:由於JDK的Selector在Linux等主流操作系統上通過epoll實現,它沒有連接句柄數的限制(只受限於操作系統的最大句柄數或者對單個進程的句柄限制),這意味着一個Selector線程可以同時處理成千上萬個客戶端連接,而且性能不會隨着客戶端的增加而線性下降,因此,它非常適合做高性能、高負載的網絡服務器。

二、AIO

JDK1.7升級了NIO類庫,升級後的NIO類庫被稱爲NIO2.0。也就是我們要介紹的AIO。NIO2.0引入了新的異步通道的概念,並提供了異步文件通道和異步套接字通道的實現。異步通道提供兩種方式獲取操作結果。

  1. 通過Java.util.concurrent.Future類來表示異步操作的結果;
  2. 在執行異步操作的時候傳入一個Java.nio.channels.
    CompletionHandler接口的實現類作爲操作完成的回調。

NIO2.0的異步套接字通道是真正的異步非阻塞IO,它對應UNIX網絡編程中的事件驅動IO(AIO),它不需要通過多路複用器(Selector)對註冊的通道進行輪詢操作即可實現異步讀寫,從而簡化了NIO的編程模型。

我們可以得出結論:異步Socket Channel是被動執行對象,我們不需要像NIO編程那樣創建一個獨立的IO線程來處理讀寫操作。對於AsynchronousServerSocketChannel和AsynchronousSocketChannel,它們都由JDK底層的線程池負責回調並驅動讀寫操作。正因爲如此,基於NIO2.0新的異步非阻塞Channel進行編程比NIO編程更爲簡單。

本文整理自《Netty權威指南

個人微信公衆號:
這裏寫圖片描述

作者:jiankunking 出處:http://blog.csdn.net/jiankunking

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