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

原文地址:http://tutorials.jenkov.com/java-nio/nio-vs-io.htmlhtml

做者:Jakob Jenkov   譯者:郭蕾    校對:方騰飛java

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

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

 

Java NIO和IO的主要區別

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

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

 

面向流與面向緩衝

Java NIO和IO之間第一個最大的區別是,IO是面向流的,NIO是面向緩衝區的。 Java IO面向流意味着每次從流中讀一個或多個字節,直至讀取全部字節,它們沒有被緩存在任何地方。此外,它不能先後移動流中的數據。若是須要先後移動從流中讀取的數據,須要先將它緩存到一個緩衝區。 Java NIO的緩衝導向方法略有不一樣。數據讀取到一個它稍後處理的緩衝區,須要時可在緩衝區中先後移動。這就增長了處理過程當中的靈活性。可是,還須要檢查是否該緩衝區中包含全部您須要處理的數據。並且,需確保當更多的數據讀入緩衝區時,不要覆蓋緩衝區裏還沒有處理的數據。網絡

 

阻塞與非阻塞IO

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

 

選擇器(Selectors

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

 

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

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

  1.  對NIO或IO類的API調用。
  2. 數據處理。
  3. 用來處理數據的線程數。

API調用

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

數據處理

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

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

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

該文本行的流能夠這樣處理:
InputStream input = … ; // get the InputStream from the client socket

1 BufferedReader reader = new BufferedReader(new InputStreamReader(input));
2  
3 String nameLine   = reader.readLine();
4 String ageLine    = reader.readLine();
5 String emailLine  = reader.readLine();
6 String phoneLine  = reader.readLine();

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

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

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

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

1 ByteBuffer buffer = ByteBuffer.allocate(48);
2  
3 int bytesRead = inChannel.read(buffer);
4  
5 while(! bufferFull(bytesRead) ) {
6  
7 bytesRead = inChannel.read(buffer);
8  
9 }

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

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

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

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

3) 用來處理數據的線程數

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

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

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

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

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

原創文章,轉載請註明: 轉載自併發編程網 – ifeve.com本文連接地址: Java NIO系列教程(十二) Java NIO與IO

 

Related Posts:

  1. Java NIO系列教程(十) Java NIO DatagramChannel
  2. Java NIO系列教程(二) Channel
  3. Java NIO系列教程(三) Buffer
  4. Java NIO系列教程(七) FileChannel
  5. Java NIO系列教程(十一) Pipe
  6. Java NIO系列教程(八) SocketChannel
  7. Java NIO系列教程(九) ServerSocketChannel
  8. Java NIO系列教程(六) Selector
  9. Java NIO系列教程(四) Scatter/Gather
  10. Java NIO系列教程(十 五)Java NIO Path
  11. Java NIO系列教程(五) 通道之間的數據傳輸
  12. Java NIO系列教程(一) Java NIO 概述
  13. Java IO: InputStream
  14. Java IO: InputStreamReader和OutputStreamWriter
  15. Java IO: 字符流的Buffered和Filter
相關文章
相關標籤/搜索