JAVA NIO vs IO
當我們學習了Java NIO和IO后,我們很快就會思考一個問題:
什么時候應該使用IO,什么時候我應該使用NIO
在下文中我會嘗試用例子闡述java NIO 和IO的區別,以及它們對你的設計會有什么影響
Java NIO和IO的主要區別
IO | NIO |
面向Stream | 面向Buffer |
阻塞IO | 非阻塞IO |
Selectors |
面向Stream和面向Buffer
Java NIO和IO之間最大的區別是IO是面向流(Stream)的,NIO是面向塊(buffer)的,所以,這意味着什么?
面向流意味着從流中一次可以讀取一個或多個字節,拿到讀取的這些做什么你說了算,這里沒有任何緩存(這里指的是使用流沒有任何緩存,接收或者發送的數據是緩存到操作系統中的,流就像一根水管從操作系統的緩存中讀取數據)而且只能順序從流中讀取數據,如果需要跳過一些字節或者再讀取已經讀過的字節,你必須將從流中讀取的數據先緩存起來。
面向塊的處理方式有些不同,數據是先被 讀/寫到buffer中的,根據需要你可以控制讀取什么位置的數據。這在處理的過程中給用戶多了一些靈活性,然而,你需要額外做的工作是檢查你需要的數據是否已經全部到了buffer中,你還需要保證當有更多的數據進入buffer中時,buffer中未處理的數據不會被覆蓋
阻塞IO和非阻塞IO
所有的Java IO流都是阻塞的,這意味着,當一條線程執行read()或者write()方法時,這條線程會一直阻塞知道讀取到了一些數據或者要寫出去的數據已經全部寫出,在這期間這條線程不能做任何其他的事情
java NIO的非阻塞模式(Java NIO有阻塞模式和非阻塞模式,阻塞模式的NIO除了使用Buffer存儲數據外和IO基本沒有區別)允許一條線程從channel中讀取數據,通過返回值來判斷buffer中是否有數據,如果沒有數據,NIO不會阻塞,因為不阻塞這條線程就可以去做其他的事情,過一段時間再回來判斷一下有沒有數據
NIO的寫也是一樣的,一條線程將buffer中的數據寫入channel,它不會等待數據全部寫完才會返回,而是調用完write()方法就會繼續向下執行
Selectors
Java NIO的selectors允許一條線程去監控多個channels的輸入,你可以向一個selector上注冊多個channel,然后調用selector的select()方法判斷是否有新的連接進來或者已經在selector上注冊時channel是否有數據進入。selector的機制讓一個線程管理多個channel變得簡單。
NIO和IO對應用的設計有何影響
選擇使用NIO還是IO做你的IO工具對應用主要有以下幾個方面的影響
1、使用IO和NIO的API是不同的(廢話)
2、處理數據的方式
3、處理數據所用到的線程數
處理數據的方式
在IO的設計里,要一個字節一個字節從InputStream 或者Reader中讀取數據,想象你正在處理一個向下面的基於行分割的流
-
Name:Anna
-
Age: 25
-
Email: anna.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();
NIO的實現會有一些不同,下面是一個簡單的例子
-
ByteBuffer buffer = ByteBuffer.allocate( 48);
-
-
int bytesRead = inChannel.read(buffer);
想象一下,調用了read(buffer)方法后,只有半行數據被讀進了buffer,例如:“Name: An”,你能現在就處理數據嗎?當然不能。你需要等待直到至少一整行數據被讀到buffer中,在這之前確保程序不要處理buffer中的數據
你如何知道buffer中是否有足夠的數據可以被處理呢?你不知道,唯一的方法就是檢查buffer中的數據。可能你會進行幾次無效的檢查(檢查了幾次數據都不夠進行處理),這會令程序設計變得比較混亂復雜
-
ByteBuffer buffer = ByteBuffer.allocate( 48);
-
-
int bytesRead = inChannel.read(buffer);
-
-
while(! bufferFull(bytesRead) ) {
-
bytesRead = inChannel.read(buffer);
-
}
is-data-in-buffer-ready 循環柱狀圖如下
總結
NIO允許你用一個單獨的線程或幾個線程管理很多個channels(網絡的或者文件的),代價是程序的處理和處理IO相比更加復雜
如果你需要同時管理成千上萬的連接,但是每個連接只發送少量數據,例如一個聊天服務器,用NIO實現會更好一些,相似的,如果你需要保持很多個到其他電腦的連接,例如P2P網絡,用一個單獨的線程來管理所有出口連接是比較合適的
如果你只有少量的連接但是每個連接都占有很高的帶寬,同時發送很多數據,傳統的IO會更適合
NIO圖解