現代的酒店服務方式跟傳統的區別有兩個:
一、增長了一個角色,要有一個專門負責收集客人需求的人。NIO裏對應的就是Selector。
二、由阻塞服務方式改成非阻塞服務了,客人吃着的時候服務員不用一直侯在客人旁邊了。傳統的IO操做,好比read(),當沒有數據可讀的時候,線程一直阻塞被佔用,直到數據到來。NIO中沒有數據可讀時,read()會當即返回0,線程不會阻塞。
NIO中,客戶端建立一個鏈接後,先要將鏈接註冊到Selector,至關於客人進入餐廳後,告訴前臺你要用餐,前臺會告訴你你的桌號是幾號,而後你就可能到那張桌子坐下了,SelectionKey就是桌號。當某一桌須要服務時,前臺就記錄哪一桌須要什麼服務,好比1號桌要點菜,2號桌要結賬,服務員從前臺取一條記錄,根據記錄提供服務,完了再來取下一條。這樣服務的時間就被最有效的利用起來了。java
區別:面試
IO | NIO |
面向Stream | 面向Buffer |
阻塞IO | 非阻塞IO |
Selectors |
Java NIO提供了與標準IO不一樣的IO工做方式:
Channels and Buffers(通道和緩衝區):標準的IO基於字節流和字符流進行操做的,而NIO是基於通道(Channel)和緩衝區(Buffer)進行操做,數據老是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。
Asynchronous IO(異步IO):Java NIO可讓你異步的使用IO,例如:當線程從通道讀取數據到緩衝區時,線程仍是能夠進行其餘事情。當數據被寫入到緩衝區時,線程能夠繼續處理它。從緩衝區寫入通道也相似。
Selectors(選擇器):Java NIO引入了選擇器的概念,選擇器用於監聽多個通道的事件(好比:鏈接打開,數據到達)。所以,單個的線程能夠監聽多個數據通道。緩存
面向Stream和麪向Buffer服務器
Java NIO和IO之間最大的區別是IO是面向流(Stream)的,NIO是面向塊(buffer)的,因此,這意味着什麼?網絡
面向流意味着從流中一次能夠讀取一個或多個字節,拿到讀取的這些作什麼你說了算,這裏沒有任何緩存(這裏指的是使用流沒有任何緩存,接收或者發送的數據是緩存到操做系統中的,流就像一根水管從操做系統的緩存中讀取數據)並且只能順序從流中讀取數據,若是須要跳過一些字節或者再讀取已經讀過的字節,你必須將從流中讀取的數據先緩存起來。異步
面向塊的處理方式有些不一樣,數據是先被 讀/寫到buffer中的,根據須要你能夠控制讀取什麼位置的數據。這在處理的過程當中給用戶多了一些靈活性,然而,你須要額外作的工做是檢查你須要的數據是否已經所有到了buffer中,你還須要保證當有更多的數據進入buffer中時,buffer中未處理的數據不會被覆蓋socket
阻塞IO和非阻塞IOide
全部的Java IO流都是阻塞的,這意味着,當一條線程執行read()或者write()方法時,這條線程會一直阻塞知道讀取到了一些數據或者要寫出去的數據已經所有寫出,在這期間這條線程不能作任何其餘的事情工具
java NIO的非阻塞模式(Java NIO有阻塞模式和非阻塞模式,阻塞模式的NIO除了使用Buffer存儲數據外和IO基本沒有區別)容許一條線程從channel中讀取數據,經過返回值來判斷buffer中是否有數據,若是沒有數據,NIO不會阻塞,由於不阻塞這條線程就能夠去作其餘的事情,過一段時間再回來判斷一下有沒有數據spa
NIO的寫也是同樣的,一條線程將buffer中的數據寫入channel,它不會等待數據所有寫完纔會返回,而是調用完write()方法就會繼續向下執行
Selectors
Java NIO的selectors容許一條線程去監控多個channels的輸入,你能夠向一個selector上註冊多個channel,而後調用selector的select()方法判斷是否有新的鏈接進來或者已經在selector上註冊時channel是否有數據進入。selector的機制讓一個線程管理多個channel變得簡單。
NIO和IO對應用的設計有何影響
選擇使用NIO仍是IO作你的IO工具對應用主要有如下幾個方面的影響
一、使用IO和NIO的API是不一樣的(廢話)
二、處理數據的方式
三、處理數據所用到的線程數
處理數據的方式
在IO的設計裏,要一個字節一個字節從InputStream 或者Reader中讀取數據,想象你正在處理一個向下面的基於行分割的流
Name:Anna Age: 25 Email: 50330690@qq.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()阻塞知道一整行都被讀取
NIO的實現會有一些不一樣,下面是一個簡單的例子
ByteBuffer buffer = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buffer);
注意第二行從channel中讀取數據到ByteBuffer,當這個方法返回你不知道是否你須要的全部數據都被讀到buffer了,你所知道的一切就是有一些數據被讀到了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); }
bufferFull方法負責檢查有多少數據被讀到了buffer中,根據返回值是true仍是false來判斷數據是否夠進行處理。bufferFull方法掃描buffer但不能改變buffer的內部狀態
is-data-in-buffer-ready 循環柱狀圖以下
總結
NIO容許你用一個單獨的線程或幾個線程管理不少個channels(網絡的或者文件的),代價是程序的處理和處理IO相比更加複雜
若是你須要同時管理成千上萬的鏈接,可是每一個鏈接只發送少許數據,例如一個聊天服務器,用NIO實現會更好一些,類似的,若是你須要保持不少個到其餘電腦的鏈接,例如P2P網絡,用一個單獨的線程來管理全部出口鏈接是比較合適的
若是你只有少許的鏈接可是每一個鏈接都佔有很高的帶寬,同時發送不少數據,傳統的IO會更適合