在 JDK 1. 4 中 新 加入 了 NIO( New Input/ Output) 類, 引入了一種基於通道和緩衝區的 I/O 方式,它可使用 Native 函數庫直接分配堆外內存,而後經過一個存儲在 Java 堆的 DirectByteBuffer 對象做爲這塊內存的引用進行操做,避免了在 Java 堆和 Native 堆中來回複製數據。安全
NIO 是一種同步非阻塞的 IO 模型。同步是指線程不斷輪詢 IO 事件是否就緒,非阻塞是指線程在等待 IO 的時候,能夠同時作其餘任務。同步的核心就是 Selector,Selector 代替了線程自己輪詢 IO 事件,避免了阻塞同時減小了沒必要要的線程消耗;非阻塞的核心就是通道和緩衝區,當 IO 事件就緒時,能夠經過寫道緩衝區,保證 IO 的成功,而無需線程阻塞式地等待。服務器
NIO是基於緩衝區的IO方式。當一個連接創建完成後,IO的數據未必會立刻到達,爲了使數據到達時可以正確完成IO操做,在BIO(阻塞IO)中,等待IO的線程必須被阻塞,以全天候地執行IO操做。爲了解決這種IO方式低效的問題,引入了緩衝區的概念,當數據到達時,能夠預先被寫入緩衝區,再由緩衝區交給線程,所以線程無需阻塞地等待。網絡
經常使用緩衝區類型:app
ByteBuffer、MappedByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer 函數
緩衝區經常使用方法:spa
allocate() - 分配一塊緩衝區 線程
put() - 向緩衝區寫數據code
get() - 向緩衝區讀數據 server
filp() - 將緩衝區從寫模式切換到讀模式 對象
clear() - 從讀模式切換到寫模式,不會清空數據,但後續寫數據會覆蓋原來的數據,即便有部分數據沒有讀,也會被遺忘;
compact() - 從讀數據切換到寫模式,數據不會被清空,會將全部未讀的數據copy到緩衝區頭部,後續寫數據不會覆蓋,而是在這些數據以後寫數據
mark() - 對position作出標記,配合reset使用
reset() - 將position置爲標記值
緩衝區的一些屬性:
capacity - 緩衝區大小,不管是讀模式仍是寫模式,此屬性值不會變;
position - 寫數據時,position表示當前寫的位置,每寫一個數據,會向下移動一個數據單元,初始爲0;最大爲capacity - 1;切換到讀模式時,position會被置爲0,表示當前讀的位置
limit - 寫模式下,limit 至關於capacity 表示最多能夠寫多少數據,切換到讀模式時,limit 等於原先的position,表示最多能夠讀多少數據。
通道是 I/O 傳輸發生時經過的入口,而緩衝區是這些數據傳輸的來源或目標。對於離開緩衝區的傳輸,您想傳遞出去的數據被置於一個緩衝區,被傳送到通道。對於傳回緩衝區的傳輸,一個通道將數據放置在您所提供的緩衝區中。
例如:有一個服務器通道 ServerSocketChannel serverChannel,一個客戶端通道 SocketChannel clientChannel;服務器緩衝區:serverBuffer,客戶端緩衝區:clientBuffer。當服務器想向客戶端發送數據時,須要調用:clientChannel.write(serverBuffer)。當客戶端要讀時,調用 clientChannel.read(clientBuffer);當客戶端想向服務器發送數據時,須要調用:serverChannel.write(clientBuffer)。當服務器要讀時,調用 serverChannel.read(serverBuffer)。
經常使用通道類型
FileChannel:從文件中讀寫數據。
DatagramChannel:能經過UDP讀寫網絡中的數據。
SocketChannel:能經過TCP讀寫網絡中的數據。
ServerSocketChannel:能夠監聽新進來的TCP鏈接,像Web服務器那樣。對每個新進來的鏈接都會建立一個SocketChannel。
通道和緩衝區的機制,使得線程無需阻塞地等待IO事件的就緒,可是老是要有人來監管這些IO事件。這個工做就交給了selector來完成,這就是所謂的同步。要使用Selector,得向Selector註冊Channel,而後調用它的select()方法。這個方法會一直阻塞到某個註冊的通道有事件就緒,這就是所說的輪詢。一旦這個方法返回,線程就能夠處理這些事件。
通道向選擇器註冊時,須要指定感興趣的事件,選擇器支持如下事件:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE
使用nio簡單實現了文件的複製。
public class Test { public static void main(String[] args) { FileInputStream fin = null; FileOutputStream fout = null; FileChannel fic = null; FileChannel foc = null; try { fin = new FileInputStream("F:\\1.txt"); fout = new FileOutputStream("F:\\2.txt"); //從FileInputStream建立用於輸入的FileChannel fic = fin.getChannel(); //從FileOutputStream建立用於輸出的FileChannel foc = fout.getChannel(); //創建buffer緩衝區,2的8次方 ByteBuffer buf = ByteBuffer.allocate(1024<<8); //根據read返回實際獨處的字節數,終止循環 //緩衝區從fic讀取數據 while(fic.read(buf)>0) { // 緩衝區翻轉用於輸出數據到focus buf.flip(); foc.write(buf); //清空緩衝區用於下次讀取 buf.clear(); } //安全釋放資源 if(fic != null) fic.close(); if(foc != null) foc.close(); if(fin != null) fin.close(); if(fout != null) fout.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { } } }
PS:因本人能力有限,若有誤還請諒解;