一、概念
??NIO即New IO,這個庫是在JDK1.4中才引入的,NIO和IO有相同的作用和目的,但實作方式不同,NIO主要用到的是塊,所以NIO的效率要比IO高很多,在Java API中提供了兩套NIO,一套是針對標準輸入輸出NIO,另一套就是網路編程NIO,
二、NIO和IO的主要區別
下表總結了Java IO和NIO之間的主要區別:

1、面向流與面向緩沖
?? Java IO和NIO之間第一個最大的區別是,IO是面向流的,NIO是面向緩沖區的, Java IO面向流意味著每次從流中讀一個或多個位元組,直至讀取所有位元組,它們沒有被快取在任何地方,此外,它不能前后移動流中的資料,如果需要前后移動從流中讀取的資料,需要先將它快取到一個緩沖區, Java NIO的緩沖導向方法略有不同,資料讀取到一個它稍后處理的緩沖區,需要時可在緩沖區中前后移動,這就增加了處理程序中的靈活性,但是,還需要檢查是否該緩沖區中包含所有您需要處理的資料,而且,需確保當更多的資料讀入緩沖區時,不要覆寫緩沖區里尚未處理的資料,
2、阻塞與非阻塞IO
?? Java IO的各種流是阻塞的,這意味著,當一個執行緒呼叫read() 或 write()時,該執行緒被阻塞,直到有一些資料被讀取,或資料完全寫入,該執行緒在此期間不能再干任何事情了,Java NIO的非阻塞模式,使一個執行緒從某通道發送請求讀取資料,但是它僅能得到目前可用的資料,如果目前沒有資料可用時,就什么都不會獲取,而不是保持執行緒阻塞,所以直至資料變的可以讀取之前,該執行緒可以繼續做其他的事情, 非阻塞寫也是如此,一個執行緒請求寫入一些資料到某通道,但不需要等待它完全寫入,這個執行緒同時可以去做別的事情, 執行緒通常將非阻塞IO的空閑時間用于在其它通道上執行IO操作,所以一個單獨的執行緒現在可以管理多個輸入和輸出通道(channel),
3、選擇器(Selectors)
?? Java NIO的選擇器允許一個單獨的執行緒來監視多個輸入通道,你可以注冊多個通道使用一個選擇器,然后使用一個單獨的執行緒來“選擇”通道:這些通道里已經有可以處理的輸入,或者選擇已準備寫入的通道,這種選擇機制,使得一個單獨的執行緒很容易來管理多個通道,
三、NIO和IO如何影回應用程式的設計
無論您選擇IO或NIO工具箱,可能會影響您應用程式設計的以下幾個方面:
1.對NIO或IO類的API呼叫,
2.資料處理,
3.用來處理資料的執行緒數,
1、API呼叫
當然,使用NIO的API呼叫時看起來與使用IO時有所不同,但這并不意外,因為并不是僅從一個InputStream逐位元組讀取,而是資料必須先讀入緩沖區再處理,
2、資料處理
使用純粹的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()呼叫回傳的時候,你知道這行包含年齡等, 正如你可以看到,該處理程式僅在有新資料讀入時運行,并知道每步的資料是什么,一旦正在運行的執行緒已處理過讀入的某些資料,該執行緒不會再回退資料(大多如此),下圖也說明了這條原則:

而一個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()方法被呼叫之前狀態相同,如果沒有,下一個讀入緩沖區的資料可能無法讀到正確的位置,這是不可能的,但卻是需要注意的又一問題,
如果緩沖區已滿,它可以被處理,如果它不滿,并且在你的實際案例中有意義,你或許能處理其中的部分資料,但是許多情況下并非如此,下圖展示了“緩沖區資料回圈就緒”:

四、總結
NIO可讓您只使用一個(或幾個)單執行緒管理多個通道(網路連接或檔案),但付出的代價是決議資料可能會比從一個阻塞流中讀取資料更復雜,
如果需要管理同時打開的成千上萬個連接,這些連接每次只是發送少量的資料,例如聊天服務器,實作NIO的服務器可能是一個優勢,同樣,如果你需要維持許多打開的連接到其他計算機上,如P2P網路中,使用一個單獨的執行緒來管理你所有出站連接,可能是一個優勢,一個執行緒多個連接的設計方案如下圖所示:

Java NIO: 單執行緒管理多個連接
如果你有少量的連接使用非常高的帶寬,一次發送大量的資料,也許典型的IO服務器實作可能非常契合,下圖說明了一個典型的IO服務器設計:

Java IO: 一個典型的IO服務器設計- 一個連接通過一個執行緒處理.
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/260102.html
標籤:其他
