Java NIO 概述

在 java 1.4 以前, Java 對 I/O 的支持並不完善,開發人員在開發高性能 I/O 程序的時候,會面臨一些巨大的
挑戰和困難,主要問題如下:

  • 沒有數據緩衝區, I/O 性能存在問題;
  • 沒有 C 或者 C++ 中的 Channel 概念,只有輸入和輸出流
  • 同步阻塞式 I/O 通信(BIO),通常會導致通信線程被長時間阻塞
  • 支持的字符集有限,硬件可移植性不好。

所以在 JDK 1.4 以後, Java 支持非阻塞的 I/O 通信模型的類庫,被稱爲 NIO。可以理解爲 New IO(新 I/O 模型),也可以理解成Not Blocking I/O(非阻塞 I/O 模型).

1、幾個概念

在理解 BIO 與 NIO 之前,我們需要明白幾個概念:

1.1 阻塞(Block)和非阻塞(Non-Block)

阻塞和非阻塞是進程在訪問數據的時候,數據內是否準備就緒的一種處理方式,當數據沒有準備的時候

阻塞:往往需要等待緩衝區中的數據準備好過後才處理其他的事情,否則一直等待在那裏

非阻塞:當我們的進程訪問我們的數據緩衝區的時候數據沒有準備好的時候直接返回不需要等待.數據有的時候也直接返回.

1.2 同步(Synchronization)和異步(Asynchronous)

同步和異步都是基於應用程序和操作系統處理 IO 事件所採用的方式.比如同步應用程序要直接參與IO讀寫的操作,異步:所有的IO讀寫交給操作系統去處理.

同步:在處理IO事件的時候 必須阻塞在某個方法上面等待我們的IO事件完成(阻塞IO事件或則通過輪詢 IO 事件的方式)

異步:所有的IO讀寫都交給了操作系統.這個時候我們可以去做其他的事情並不需要去完成真正的 IO 操作,當操作完成IO後給我們的應用程序一個通知就可以了。

2、NIO 與 IO

NIO 與 普通 IO 的主要區別如下:

IO NIO
面向流 (Stream oriented) 面向緩衝區(Buffer oriented)
阻塞式 IO (Blocking IO) 非阻塞式 (Non blocking IO)
多路複用器 (Selectors)

2.1 面向流與面向緩衝

Java NIO 和 IO之間第一個最大的區別是,IO 是面向流的,NIO 是面向緩衝區的。

Java IO 面向流意味着每次從流中讀一個或多個字節,直至讀取所有字節,它們沒有被緩存在任何地方。此外,它不能前後移動流中的數據。如果需要前後移動從流中讀取的數據,需要先將它緩存到一個緩衝區。

Java NIO的緩衝導向方法略有不同。數據讀取到一個它稍後處理的緩衝區,需要時可在緩衝區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩衝區中包含所有您需要處理的數據。而且,需確保當更多的數據讀入緩衝區時,不要覆蓋緩衝區裏尚未處理的數據。

2.2 阻塞與非阻塞IO

Java IO的各種流是阻塞的。這意味着,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入。該線程在此期間不能再幹任何事情了。

Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什麼都不會獲取。而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。 線程通常將非阻塞IO的空閒時間用於在其它通道上執行IO操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。

2.3 多路複用器(Selectors)

Java NIO的選擇器允許一個單獨的線程來監視多個輸入通道,你可以註冊多個通道使用一個選擇器,然後使用一個單獨的線程來“選擇”通道:這些通道里已經有可以處理的輸入,或者選擇已準備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道。

3、NIO 核心概念

Java Nio 由以下三個核心的概念組成:

  • Buffer : 緩衝區
  • Channel : 通道
  • Selector : 多路複用器

3.1 Buffer

緩衝區實際上是一個容器對象,更直接的說,其實就是一個數組,在NIO庫中,所有數據都是用緩衝區處理的。在讀取數據時,它是直接讀到緩衝區中的; 在寫入數據時,它也是寫入到緩衝區中的;任何時候訪問 NIO 中的數據,都是將它放到緩衝區中。而在面向流I/O系統中,所有數據都是直接寫入或者直接將數據讀取到Stream對象中。
在NIO中,所有的緩衝區類型都繼承於抽象類Buffer

3.2 Channel

通道是一個對象,通過它可以讀取和寫入數據,當然了所有數據都通過Buffer對象來處理。我們永遠不會將字節直接寫入通道中,相反是將數據寫入包含一個或者多個字節的緩衝區。同樣不會直接從通道中讀取字節,而是將數據從通道讀入緩衝區,再從緩衝區獲取這個字節。
在NIO中,提供了多種通道對象,而所有的通道對象都實現了Channel接口。

3.3 Selector

BIO 進行socket編程時,accept方法會一直阻塞,直到有客戶端請求的到來,並返回socket進行相應的處理。整個過程是流水線的,處理完一個請求,才能去獲取並處理後面的請求,當然也可以把獲取socket和處理socket的過程分開,一個線程負責accept,一個線程池負責處理請求。

這裏寫圖片描述

NIO 中非阻塞 I/O 採用了基於Reactor 模式的工作方式,I/O 調用不會被阻塞,相反是註冊感興趣的特定I/O事件,如可讀數據到達,新的套接字連接等等,在發生特定事件時,系統再通知我們。NIO中實現非阻塞I/O的核心對象就是Selector,Selector就是註冊各種I/O事件地方,而且當那些事件發生時,就是這個對象告訴我們所發生的事件。

這裏寫圖片描述

當有讀或寫等任何註冊的事件發生時,可以從Selector中獲得相應的SelectionKey,同時從 SelectionKey中可以找到發生的事件和該事件所發生的具體的SelectableChannel,以獲得客戶端發送過來的數據。

4、總結

希望通過以上的講解大家能夠理解 NIO 出現的原因以及 NIO 與 BIO 的區別,同時也能夠理解 NIO 裏面的核心概念。後續會具體分析 NIO 裏面的核心概念達到深入理解 Java 裏的 NIO。

參考文章:

併發編程網 – Java NIO 教程
Netty權威指南 – 李林鋒

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