它是Java1.4以後出現的IO API,與傳統IO和網絡API不一樣,具備非阻塞的特色。前端
在BIO中咱們使用字節流和字符流。NIO中咱們使用channel和buffer。數據老是從一個channel中讀取到buffer中,或者從buffer中寫入到channel中。web
NIO的意思是一個線程可讓一個channel將數據讀取到buffer中,與此同時,這個線程還能夠作其餘的事情,線程能夠等到數據所有進入buffer以後再處理數據,從buffer中寫入線程也是同樣的。數組
selector:選擇器是一個NIO當中的概念,指的是一個對象,能監視多個channel發生的事件(如鏈接創建,數據到達等)。所以,一個單線程能夠監視多個channel的數據。服務器
Java NIO的三個核心基礎組件,網絡
其他的諸如Pipe,FileLcok都是在使用以上三個核心組件時幫助更好使用的工具類。app
全部的IO操做在NIO中都是以Channel開始的。一個Channel就像一個流。從Channel中,數據能夠被讀取到buffer裏,也能夠從buffer裏寫到Channel中。dom
基本的Channel實現有如下這些:異步
涵蓋了UDP,TCP以及文件的IO操做。函數
核心的buffer實現有這些工具
涵蓋了全部的基本數據類型(4類8種,除了Boolean)。也有其餘的buffer如MappedByteBuffer,此處不講。
selector容許一個線程來監視多個Channel,這在當你的應用創建了多個鏈接,可是每一個鏈接吞吐量都較小的時候是可行的。例如:一個聊天服務器。圖爲一個線程使用selector處理三個channel。
要使用一個Selector,你要先註冊這個selector的Channels。而後你調用selector的select()方法。這個方法會阻塞,直到它註冊的channels當中有一個準備好了的事件發生了。當select()方法返回的時候,線程能夠處理這些事件,如新的鏈接的到來,數據收到了等。
NIO channel和流很近似可是也有一些不一樣。
如下是NIO中最重要的幾個channel的實現。
使用一個FileChannel將數據讀入一個buffer
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); while (bytesRead != -1) { System.out.println("Read " + bytesRead); buf.flip(); while(buf.hasRemaining()){ System.out.print((char) buf.get()); } buf.clear(); bytesRead = inChannel.read(buf); } aFile.close();
buf.flip()的意思是讀寫轉換,首先你讀入一個buffer,而後你flip,轉換讀寫,而後再從buffer中讀出,buffer的操做接下來會講。
NIO buffer在與NIO Channel交互時使用,數據從channel中讀取出來放入buffer,或者從buffer中讀取出來寫入channel。
buffer就是一塊內存,你能夠寫入數據,而且在以後讀取它。這塊內存被包裝成NIO buffer對象,它提供了一些方法來讓你更簡單地操做內存。
使用buffer讀寫數據基本上分爲如下4部操做:
在寫buffer的時候,buffer會跟蹤寫入了多少數據,須要讀buffer的時候,須要調用flip()來將buffer從寫模式切換成讀模式,讀模式中只能讀取寫入的數據,而非整個buffer。
當數據都讀完了,你須要清空buffer以供下次使用,能夠有2種方法來操做:
區別:clear方法清空整個buffer,compact方法只清除你已經讀取的數據,未讀取的數據會被移到buffer的開頭,此時寫入數據會從當前數據的末尾開始。
一個簡單的buffer使用例子:
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); //建立一個容量爲48的ByteBuffer ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); //從channel中讀(取數據而後寫)入buffer //下面是讀取buffer while (bytesRead != -1) { buf.flip(); //轉換buffer爲讀模式 while(buf.hasRemaining()){ System.out.print((char) buf.get()); // 一次讀取一個byte } buf.clear(); //清空buffer準備下一次寫入 bytesRead = inChannel.read(buf); } aFile.close();
buffer有3個屬性須要熟悉以理解buffer的工做原理:
position和limit的值在讀/寫模式中是不同的。
capacity的值永遠表示buffer的大小。
下圖解釋了在讀/寫模式中Capacity,Position和Limit的意思。
Java NIO中有如下這些buffer種類:
得到一個buffer 以前必須先分配一塊內存,每一個buffer類都有一個靜態方法allocate() 來作這件事。
下例爲建立一個容量爲48byte的ByteBuffer:ByteBuffer buf = ByteBuffer.allocate(48);
建立一個1024個字符的CharBufferCharBuffer buf = CharBuffer.allocate(1024);
寫入buffer的方法有2種:
例:
int bytesRead = inChannel.read(buf); //從channel讀入buffer
buf.put(127); //自行寫入buffer
put方法有不少的重載形式。以供你用各類不一樣的方法寫入buffer中,好比從一個特定的position,或者寫入一個array,詳見JavaDoc。
flip方法將寫模式切換成讀模式,調用flip()方法會將limit設置爲position,將position設置回0。
換句話說,position標誌着寫模式中寫到哪裏,切換成讀模式以後,limit標誌着以前寫到哪裏,也就是如今能讀到哪裏。
有2種方法能夠從buffer中讀取數據。
例子:
//從buffer中讀取數據到channel中 int bytesWritten = inChannel.write(buf); //使用buffer的get()方法自行從buffer中讀出數據 byte aByte = buf.get();
get方法有不少的重載形式。以供你用各類不一樣的方法讀取buffer中的數據。例如從特定位置讀取數據,或者讀一個數組出來。詳見JavaDoc。
rewind()方法將position設置爲0,可是不會動buffer裏的數據,這樣能夠從頭開始從新讀取數據,limit的值不會變,這意味着limit依舊標誌着能讀多少數據。
當你讀完全部的數據想要從新寫入數據時,你能夠調用clear或者compact方法。
當你調用clear()方法的時候,position被設置爲0,limit被設置爲capacity,換句話說,buffer的數據雖然都還在,可是buffer被初始化了,處於能夠被重寫的狀態。
這也就意味着若是buffer中還有沒被讀取的數據,在執行clear以後,你沒法知道數據讀到哪兒了,剩下的數據還有多少。
若是還有沒有讀完的數據,可是你想先寫數據,能夠用compact()方法,這樣未讀數據會放在buffer前端,能夠在未讀數據以後跟着寫新的數據。compact()會複製未讀數據到buffer前端,而後設置position爲未讀數據單位後面緊跟的位置。limit仍是設置爲capacity,這和clear是同樣的。如今buffer處於能夠寫的狀態,可是不會覆蓋以前未讀完的數據。
你能夠經過調用buffer.mark()來mark一個buffer中給定的位置。而後你就能夠用buffer.reset()方法來說position設置回以前mark的位置。
例子:
buffer.mark(); //調用buffer.get()方法若干次,e.g. 好比在作parsing的時候 buffer.reset(); //set position back to mark.
使用這2種方法可以比較2個buffer。
equals()
equals()方法用於判斷2個buffer是否相等,2個buffer是equal的,當它們:
如你所見,equals()方法只比較buffer的部份內容,而不是buffer中全部的數據,事實上,它只比較buffer中剩餘的元素是否同樣。
compareTo()
compareTo()方法比較兩個buffer的剩餘元素(字節,字符等),用於例如: 排序。
在下列狀況下,緩衝區被認爲比另外一個緩衝區「小」:
比較是針對每一個緩衝區你剩餘數據(從 position 到 limit)進行的,與它們在 equals() 中的方式相同,直到不相等的元素被發現或者到達緩衝區的上界。若是一個緩衝區在不相等元素髮現前已經被耗盡,較短的緩衝區被認爲是小於較長的緩衝區。
if (buffer1.compareTo(buffer2) < 0) { // do sth, it means buffer2 < buffer1,not buffer1 < buffer2 doSth(); }