java面試題之----IO與NIO的區別

JAVA NIO vs IOjava

當咱們學習了Java NIO和IO後,咱們很快就會思考一個問題:緩存

何時應該使用IO,何時我應該使用NIO服務器

在下文中我會嘗試用例子闡述java NIO 和IO的區別,以及它們對你的設計會有什麼影響網絡

Java NIO和IO的主要區別socket

 

IO NIO
面向Stream 面向Buffer
阻塞IO 非阻塞IO
  Selectors

 

面向Stream和麪向Buffer工具

Java NIO和IO之間最大的區別是IO是面向流(Stream)的,NIO是面向塊(buffer)的,因此,這意味着什麼?學習

面向流意味着從流中一次能夠讀取一個或多個字節,拿到讀取的這些作什麼你說了算,這裏沒有任何緩存(這裏指的是使用流沒有任何緩存,接收或者發送的數據是緩存到操做系統中的,流就像一根水管從操做系統的緩存中讀取數據)並且只能順序從流中讀取數據,若是須要跳過一些字節或者再讀取已經讀過的字節,你必須將從流中讀取的數據先緩存起來spa

面向塊的處理方式有些不一樣,數據是先被 讀/寫到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工具對應用主要有如下幾個方面的影響

一、使用IO和NIO的API是不一樣的(廢話)

二、處理數據的方式

三、處理數據所用到的線程數

處理數據的方式

在IO的設計裏,要一個字節一個字節從InputStream 或者Reader中讀取數據,想象你正在處理一個向下面的基於行分割的流

 

  1.  
    Name:Anna
  2.  
    Age:  25
  3.  
    Email: anna @mailserver.com
  4.  
    Phone: 1234567890

處理文本行的流的代碼應該向下面這樣

 

  1. InputStream input = ... ;  // get the InputStream from the client socket
  2.  
  3. BufferedReader reader =  new BufferedReader(new InputStreamReader(input));
  4.  
  5. String nameLine = reader.readLine();
  6. String ageLine = reader.readLine();
  7. String emailLine = reader.readLine();
  8. String phoneLine = reader.readLine();

注意,一旦reader.readLine()方法返回,你就能夠肯定整行已經被讀取,readLine()阻塞知道一整行都被讀取

NIO的實現會有一些不一樣,下面是一個簡單的例子

 

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

注意第二行從channel中讀取數據到ByteBuffer,當這個方法返回你不知道是否你須要的全部數據都被讀到buffer了,你所知道的一切就是有一些數據被讀到了buffer中,可是你並不知道具體有多少數據,這使程序的處理變得稍微有些困難

想象一下,調用了read(buffer)方法後,只有半行數據被讀進了buffer,例如:「Name: An」,你能如今就處理數據嗎?固然不能。你須要等待直到至少一整行數據被讀到buffer中,在這以前確保程序不要處理buffer中的數據

你如何知道buffer中是否有足夠的數據能夠被處理呢?你不知道,惟一的方法就是檢查buffer中的數據。可能你會進行幾回無效的檢查(檢查了幾回數據都不夠進行處理),這會令程序設計變得比較混亂複雜

 

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

bufferFull方法負責檢查有多少數據被讀到了buffer中,根據返回值是true仍是false來判斷數據是否夠進行處理。bufferFull方法掃描buffer但不能改變buffer的內部狀態

is-data-in-buffer-ready 循環柱狀圖以下

總結

NIO容許你用一個單獨的線程或幾個線程管理不少個channels(網絡的或者文件的),代價是程序的處理和處理IO相比更加複雜

若是你須要同時管理成千上萬的鏈接,可是每一個鏈接只發送少許數據,例如一個聊天服務器,用NIO實現會更好一些,類似的,若是你須要保持不少個到其餘電腦的鏈接,例如P2P網絡,用一個單獨的線程來管理全部出口鏈接是比較合適的

 


若是你只有少許的鏈接可是每一個鏈接都佔有很高的帶寬,同時發送不少數據,傳統的IO會更適合


 

          NIO圖解

                         

相關文章
相關標籤/搜索