Java NIO系列教程(十一) Java NIO 與 IO

Java NIO系列教程(十一) Java NIO與IO

當學習了 Java NIO 和 IO 的 API 後,一個問題立刻涌入腦海:html

我應該什麼時候使用 IO,什麼時候使用 NIO 呢?在本文中,我會盡可能清晰地解析 Java NIO 和 IO 的差別、它們的使用場景,以及它們如何影響您的代碼設計。java

下表總結了 Java NIO 和 IO 之間的主要差異,我會更詳細地描述表中每部分的差別。api

IO NIO
面向流 面向緩衝
阻塞IO 非阻塞IO
選擇器

1、面向流與面向緩衝

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

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

Java NIO 的緩衝導向方法略有不一樣。數據讀取到一個它稍後處理的緩衝區,須要時可在緩衝區中先後移動。這就增長了處理過程當中的靈活性。可是,還須要檢查是否該緩衝區中包含全部您須要處理的數據。並且,需確保當更多的數據讀入緩衝區時,不要覆蓋緩衝區裏還沒有處理的數據。網絡

2、阻塞與非阻塞 IO

Java IO 的各類流是阻塞的。這意味着,當一個線程調用 read() 或 write() 時,該線程被阻塞,直到有一些數據被讀取,或數據徹底寫入。該線程在此期間不能再幹任何事情了。 Java NIO 的非阻塞模式,使一個線程從某通道發送請求讀取數據,可是它僅能獲得目前可用的數據,若是目前沒有數據可用時,就什麼都不會獲取。而不是保持線程阻塞,因此直至數據變的能夠讀取以前,該線程能夠繼續作其餘的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不須要等待它徹底寫入,這個線程同時能夠去作別的事情。 線程一般將非阻塞 IO 的空閒時間用於在其它通道上執行 IO 操做,因此一個單獨的線程如今能夠管理多個輸入和輸出通道(channel)。異步

(1) 阻塞和非阻塞工具

  • 阻塞概念:當應用程序訪問數據時,若是網絡傳輸數據很慢,那麼程序就一直等待,直到傳輸完畢爲止。學習

  • 非阻塞概念:當應用程序訪問數據緩衝區時,若是沒有數據則直接返回,無需等待。spa

注意:BIO 爲同步阻塞形式,NIO 爲同步非阻塞形式,NIO 並無實現異步。在 JDK 1.7 後,升級了 NIO 庫,支持異步非阻塞通訊模型,即 NIO2.0(AIO)。

(2) 同步和異步

同步和異步通常是基於應用程序和操做系統處理 IO 事件所採用的方式。同步是應用程序直接參與 IO 讀寫。異步是全部的 IO 讀寫都交給操做系統處理,應用程序只須要等待通知。

  • 同步時,應用程序會直接參與 IO 讀寫操做,而且咱們的應用程序會直接阻塞到某一個方法上,直到數據準備就緒;或者採用輪詢的策略實時檢查數據的就緒狀態,若是就緒則獲取數據。

  • 異步時,全部的 IO 讀寫操做交給操做系統處理,與咱們的應用程序沒有直接關係,咱們程序不須要關心 IO 讀寫,當操做系統完成 IO 讀寫操做時,會給咱們應用程序發送,應用程序直接拿走數據便可。

3、選擇器(Selectors)

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

4、NIO 和 IO 如何影響應用程序的設計

不管您選擇 IO 或 NIO 工具箱,可能會影響您應用程序設計的如下幾個方面:

  • 對 NIO 或 IO 類的API調用。
  • 數據處理。
  • 用來處理數據的線程數。

4.1 API 調用

固然,使用 NIO 的 API 調用時看起來與使用 IO 時有所不一樣,但這並不意外,由於並非僅從一個 InputStream 逐字節讀取,而是數據必須先讀入緩衝區再處理。

4.2 數據處理

使用純粹的 NIO 設計相較 IO 設計,數據處理也受到影響。

在 IO 設計中,咱們從 InputStream 或 Reader 逐字節讀取數據。假設你正在處理一基於行的文本數據流,例如:

Name: Anna
Age: 25
Email: anna@mailserver.com
Phone: 1234567890

該文本行的流能夠這樣處理:

BufferedReader reader = new BufferedReader(new InputStreamReader(input));

String nameLine   = reader.readLine();
String ageLine    = reader.readLine();
String emailLine  = reader.readLine();
String phoneLine  = reader.readLine();

請注意處理狀態由程序執行多久決定。換句話說,一旦 reader.readLine() 方法返回,你就知道確定文本行就已讀完, readline() 阻塞直到整行讀完,這就是緣由。你也知道此行包含名稱;一樣,第二個 readline() 調用返回的時候,你知道這行包含年齡等。 正如你能夠看到,該處理程序僅在有新數據讀入時運行,並知道每步的數據是什麼。一旦正在運行的線程已處理過讀入的某些數據,該線程不會再回退數據(大多如此)。下圖也說明了這條原則:

Java IO: 從一個阻塞的流中讀數據

而一個 NIO 的實現會有所不一樣,下面是一個簡單的例子:

ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buffer);

注意第二行,從通道讀取字節到 ByteBuffer。當這個方法調用返回時,你不知道你所需的全部數據是否在緩衝區內。你所知道的是,該緩衝區包含一些字節,這使得處理有點困難。
假設第一次 read(buffer) 調用後,讀入緩衝區的數據只有半行,例如,「Name:An」,你能處理數據嗎?顯然不能,須要等待,直到整行數據讀入緩存,在此以前,對數據的任何處理毫無心義。

因此,你怎麼知道是否該緩衝區包含足夠的數據能夠處理呢?好了,你不知道。發現的方法只能查看緩衝區中的數據。其結果是,在你知道全部數據都在緩衝區裏以前,你必須檢查幾回緩衝區的數據。這不只效率低下,並且可使程序設計方案雜亂不堪。例如:

ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);
while(! bufferFull(bytesRead) ) {
    bytesRead = inChannel.read(buffer);
}

bufferFull() 方法必須跟蹤有多少數據讀入緩衝區,並返回真或假,這取決於緩衝區是否已滿。換句話說,若是緩衝區準備好被處理,那麼表示緩衝區滿了。

bufferFull() 方法掃描緩衝區,但必須保持在 bufferFull() 方法被調用以前狀態相同。若是沒有,下一個讀入緩衝區的數據可能沒法讀到正確的位置。這是不可能的,但倒是須要注意的又一問題。

若是緩衝區已滿,它能夠被處理。若是它不滿,而且在你的實際案例中有意義,你或許能處理其中的部分數據。可是許多狀況下並不是如此。下圖展現了「緩衝區數據循環就緒」:

Java NIO:從一個通道里讀數據,直到全部的數據都讀到緩衝區裏

4.3 用來處理數據的線程數

NIO 可以讓您只使用一個(或幾個)單線程管理多個通道(網絡鏈接或文件),但付出的代價是解析數據可能會比從一個阻塞流中讀取數據更復雜。

若是須要管理同時打開的成千上萬個鏈接,這些鏈接每次只是發送少許的數據,例如聊天服務器,實現 NIO 的服務器多是一個優點。一樣,若是你須要維持許多打開的鏈接到其餘計算機上,如 P2P 網絡中,使用一個單獨的線程來管理你全部出站鏈接,多是一個優點。一個線程多個鏈接的設計方案以下圖所示:

Java NIO: 單線程管理多個鏈接

若是你有少許的鏈接使用很是高的帶寬,一次發送大量的數據,也許典型的IO服務器實現可能很是契合。下圖說明了一個典型的IO服務器設計:

Java IO: 一個典型的IO服務器設計- 一個鏈接經過一個線程處理.

Java NIO 系列:

  1. Java NIO系列教程(一) Java NIO 概述
  2. Java NIO系列教程(二) Channel
  3. Java NIO系列教程(三) Buffer
  4. Java NIO系列教程(四) Scatter 和 Gather
  5. Java NIO系列教程(五) 通道之間的數據傳輸
  6. Java NIO系列教程(六) Selector
  7. Java NIO系列教程(七) FileChannel
  8. Java NIO系列教程(八) SocketChannel
  9. Java NIO系列教程(九) ServerSocketChannel
  10. Java NIO系列教程(十) DatagramChannel
  11. Java NIO系列教程(十一) Java NIO 與 IO
  12. Java NIO系列教程(十二) Pipe
  13. Java NIO系列教程(十三) Path
  14. Java NIO系列教程(十四) Files

天天用心記錄一點點。內容也許不重要,但習慣很重要!

相關文章
相關標籤/搜索