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

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

作者:Jakob Jenkov ??譯者:郭蕾 ? ?校對:方騰飛

當學習了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的選擇器允許一個單獨的線程來監視多個輸入通道,你可以注冊多個通道使用一個選擇器,然后使用一個單獨的線程來“選擇”通道:這些通道里已經有可以處理的輸入,或者選擇已準備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道。

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

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:從一個通道里讀數據,直到所有的數據都讀到緩沖區里.

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

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

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

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

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

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

原創文章,轉載請注明: 轉載自并發編程網 – www.okfdzs1908.com本文鏈接地址: Java NIO系列教程(十二) Java NIO與IO


FavoriteLoading添加本文到我的收藏
  • Trackback 關閉
  • 評論 (37)
  1. 贊一個 寫得不錯!

    • ℡actionフ
    • 2013/05/07 4:38下午

    下面的兩個圖有什么區別

      • 匿名
      • 2013/11/16 4:56下午

      同問

        • gongweixin
        • 2014/02/27 5:48下午

        圖畫的不是太好 ,上面的圖是說 多個connection 對應一個thread ,下面的圖是說 每個connection 對應一個thread , 根據我的理解圖這么畫會好點、
        |——————|
        | Thread |
        |————-| |——————|
        | Thread |———————->| |————| |
        |————-| | | Connection | |
        |ServerSocket | | |————| |
        |————-| | | Connection | |
        | |————| |
        |——————|

        ====================================================================
        |——————|
        | Thread |
        |————-| |——————|
        | Thread |———————->| Connection |
        |————-| |——————|
        |ServerSocket |
        |————-|———————->|——————|
        | Thread |
        |——————|
        | Connection |
        |——————|

        • gongweixin
        • 2014/02/27 5:55下午

        圖畫的不是太好 ,上面的圖是說 多個connection 對應一個thread ,下面的圖是說 每個connection 對應一個thread , 根據我的理解圖這么畫會好點、
        |==================|
        | Thread |
        |=============| |==================|
        | Thread |———————->| |============| |
        |=============| | | Connection | |
        |ServerSocket | | |============| |
        |=============| | | Connection | |
        | |============| |
        |==================|
        +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        |==================|
        | Thread |
        |=============| |==================|
        | Thread |======================>| Connection |
        |=============| |==================|
        |ServerSocket |
        |=============|======================>|==================|
        | Thread |
        |==================|
        | Connection |
        |==================|

  2. hi, 讀完了關于 nio 的文章。 請問有沒有相關的全面點的 demo 能更深刻的理解?

  3. is-data-in-buffer-ready loop 翻譯成“緩沖區數據循環就緒”,合適嗎?

    • gongweixin
    • 2014/02/27 5:56下午

    夕水溪下 :
    截圖吧

    不會啊 、

    • 夏末冬初
    • 2014/03/14 6:39下午

    Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什么都不會獲取。而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。

    沒有數據可讀之前做別的事情,這個別的事情泛指哪些事情,假如我這個線程就需要讀出的所有數據,然后進行打印,那這時候打印出來的是部分數據,還是所有數據。

      • 木槿花蕭
      • 2014/08/31 3:42下午

      你可以寫個回調或者Future來把數據讀取完之后的處理代碼放進去,然后可以不等待數據處理完就可以做任何事情,比如繼續監聽客戶端請求之類的。 但總感覺NIO是以增加了數據處理復雜度來換取cpu的時間。

    • 夏末冬初
    • 2014/03/14 7:07下午

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

    一定要這樣不斷的輪訓檢查嗎,這樣效率貌似和阻塞io沒有啥區別啊。而且什么時候判斷數據是完整的呢?

      • 木槿花蕭
      • 2014/08/31 3:54下午

      int bytesRead = inChannel.read(buffer); 這里返回的字節數是-1的時候證明你已經讀完了,前幾篇文章有寫。至于有沒有讀取到完整數據需要你自己的代碼控制好。第三章通道的示例里面有寫:http://www.okfdzs1908.com/channels/

  4. 為什么NIO是同步非阻塞的? 同步是哪里同步?阻塞是哪里阻塞?

    • NIO的應用場景:多連接小數據。你可以這樣認為,線程處理完某一連接的一部分數據之后,下一部分數據還沒到,這樣,線程有時間先去處理另一個連接的數據。樓主的例子中沒有體現多連接的情況。

      • terryg
      • 2017/01/17 11:48下午

      NIO的同步應該是說的NIO的底層實現,有同步非阻塞和異步非阻塞兩種實現方式,參見:http://www.artima.com/articles/io_design_patterns.html

    • 在路上
    • 2014/09/30 11:48上午

    讀完整個系列,對Nio有了個大概的了解,十分感謝!

  5. 讀完整個系列,對NIO有大致的了解了,非常感謝!但是,請問,有沒有一個比較完善點的例子,能體現NIO在實際工作中的運用嗎?目前,對NIO的優勢持疑惑狀態,不能體會它的優點,望解答~

    • 可以對比使用下Java IO ,使用起來NIO比較方便,有更多更高級的API,比如transferTo三行代碼就能實現從一個文件讀取數據到另外一個文件。

  6. nio既然是異步的, 那有沒有回調函數或者defer函數呢?

    • '4 ever love
    • 2014/11/06 5:18下午

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

    這一段翻譯有問題,應該搞成:

    通過bufferFull()方法掃描緩沖區,緩沖區必須保持和bufferFull()方法被調用之前相同的狀態,如果不是,下一個讀入緩沖區的數據可能無法被讀到正確的位置,這并非不可能,但也暴露出另一個需要我們注意的問題

    • hequn
    • 2015/01/06 3:07下午

    “這是不可能的,但卻是需要注意的又一問題?!?應該是“這不是不可能的,但卻是需要注意的又一問題?!痹腡his is not impossible, but it is yet another issue to watch out for.

    • 雨藍
    • 2015/04/25 5:35下午

    nio處理多而小的連接,用了緩沖區,那么會不會時時性不好了呢?

    • jk1420
    • 2015/06/09 5:00下午

    圖片都壞了,看不到

    • sstong123
    • 2015/07/06 10:21上午

    好文章,但文中圖片都不能顯示,樓主可不可以補一下?

    • hl174
    • 2016/04/26 4:02下午

    bufferFull

    怎么沒有發現這個方法

    • hl174
    • 2016/04/26 4:07下午

    話說仔細看了這一些列12期的nio文章,還是不知道具體怎么用,有沒有深點的應用型的文章

    • jzh0535
    • 2016/06/27 7:20下午

    mark!

    • Yancy
    • 2016/09/16 10:25上午

    圖加載不了

    • bw
    • 2016/11/03 1:39下午

    這些圖怎么都不顯示啦?

    • lin_1220
    • 2017/01/12 10:57上午

    樓主,您好,里面的圖全都看不到

    • adeline
    • 2017/12/22 4:23下午

    看不到圖

    • red_and_black
    • 2018/06/07 11:11上午

    樓主,哪里有圖啊,沒有圖啊,原文中倒是有圖

    • menfre
    • 2018/09/15 2:46下午

    “bufferFull()方法掃描緩沖區,但必須保持在bufferFull()方法被調用之前狀態相同。如果沒有,下一個讀入緩沖區的數據可能無法讀到正確的位置。這是不可能的,但卻是需要注意的又一問題。”

    這里是說保持bufferFull()調用前后的緩沖區狀態(position/limit)是不可能的嗎? 可以通過檢查前 mark position的位置,在檢查完成通過reset來保證檢查前后緩沖區的狀態一致。

    • Arry0624
    • 2019/01/11 4:54下午

    QAQ~學習學習

您必須 登陸 后才能發表評論

return top

竞彩258网 eey| 7mq| eas| wi7| aqm| u7o| ikq| 7sw| sga| 8sk| gk6| yys| o6q| guk| 6qw| wkq| eg6| qwq| q7q| cgq| 7sy| oc7| cmu| y5g| qeu| 5eu| sc6| kyw| q6i| s6o| uke| 6gi| mo6| kkc| c4u| iwa| 5sm| wwc| 5im| oc5| iwy| m5c| k5e| uwe| 5ua| mm6| ayo| y4c| oqk| 4qw| so4| wwq| o4u| yyc| 4gm| 5ek| uw5| waq| q3e| aou| 3cw| es3| omc| i3o| cek| 44k| gey| 4og| 4wy| aa4| aye| g2q| ukc| 2ga| se3| esy| y3g| cqu| 3ka| ky3| wkc| egm| y1m| wmu| 2ac| es2| mou| m2g| qus| 2cg|