NIO區別於IO,是同步非阻塞的。nio直接使用native函數庫,直接分配對外內存,經過DirectByteBuffer對象做爲這塊內存的引用操做,避免了數據在java堆和對外內存間來回複製。java
概念區分:linux
1.同步:使用同步IO時,Java本身處理IO讀寫。編程
2.異步:使用異步IO時,Java將IO讀寫委託給OS處理,須要將數據緩衝區地址和大小傳給OS,完成後OS通知Java處理(回調)。數組
3.阻塞:使用阻塞IO時,Java調用會一直阻塞到讀寫完成才返回。緩存
4.非阻塞:使用非阻塞IO時,若是不能立馬讀寫,Java調用會立刻返回,當IO事件分發器通知可讀寫時在進行讀寫,不斷循環直到讀寫完成。網絡
BIO:同步且阻塞 NIO:同步非阻塞 AIO:異步非阻塞併發
應用場景:併發鏈接數很少時採用BIO,由於它編程和調試都很是簡單,但若是涉及到高併發的狀況,應選擇NIO或AIO,更好的建議是採用成熟的網絡通訊框架Netty。框架
NIO主要組件:dom
Channel 通道,文件數據首先存在於channel中 可經過FileInputStream.getChannel()獲取到channel異步
Buffer 緩衝區,數據能夠從通道讀入緩衝區,也能夠將數據從緩衝區寫到通道中
Selector 用於管理一個或多個通道
nio讀取文件示例
1 public class TestChannel { 2 public static void main(String[] args) throws IOException { 3 //讀取到文件 4 RandomAccessFile f = new RandomAccessFile("C:\\test-nio.txt", "rw"); 5 //獲取channel 6 FileChannel channel = f.getChannel(); 7 //分配緩存大小 8 ByteBuffer buffer = ByteBuffer.allocate(1024); 9 //從channel中讀取數據循環讀取到緩存中 10 int bytesRead = channel.read(buffer); 11 while (bytesRead != -1){ 12 System.out.println("before flip:"+buffer); 13 //切換爲讀模式,反轉pos爲0,使下一步get從0開始 14 buffer.flip(); 15 System.out.println("after flip:"+buffer); 16 while (buffer.hasRemaining()){ 17 System.out.print((char)buffer.get()); 18 } 19 //上一步打印完成後清除緩存內數據,使緩衝區能夠再次被寫入 20 buffer.clear(); 21 System.out.println("\nafter clear:"+buffer); 22 23 bytesRead = channel.read(buffer); 24 } 25 //關閉通道 26 channel.close();29 } 30 }
將字符串持久化示例
public class TestWriteChannel { public static void main(String[] args) throws IOException { RandomAccessFile file = new RandomAccessFile("c:/wriate-test.txt","rw"); FileChannel fileChannel = file.getChannel(); String data = "我wjeabcdefghijklmnopqrstuvwxyz"+System.currentTimeMillis(); System.out.println(data.getBytes().length); //注意buffer分配的內存空間要大於data.getBytes().length,否者報BufferOverflowException異常 ByteBuffer buffer = ByteBuffer.allocate(88); buffer.clear(); buffer.put(data.getBytes()); //切換爲讀模式 pos=0 buffer.flip(); while (buffer.hasRemaining()){ fileChannel.write(buffer); } fileChannel.close(); } }
幾個方法說明:
buffer
flip() 將buffer從寫模式切換爲讀模式,是pos=0 limit=「寫模式時最後的pos「。limit表示此緩衝區中最多能夠讀取的字節數
clear() 清空緩衝區,使pos=0 limit=capaticy
compact() 將全部未讀的數據拷貝到Buffer起始處。而後將position設到最後一個未讀元素正後面。注意和clear區分,clear不處理未讀數據,直接清空
rewind() 將position設回0,因此你能夠重讀Buffer中的全部數據。limit保持不變,仍然表示能從Buffer中讀取多少
mark()/reset() 經過調用Buffer.mark()方法,能夠標記Buffer中的一個特定position。以後能夠經過調用Buffer.reset()方法恢復到這個position
put() 將字節存放到buffer中
get() 從buffer中讀取字節
channel
size() channel關聯的文件的大小
position() 獲取當前的位置,加上參數則是設置位置
truncate() 截取文件,如truncate(1024)截取文件的的前1024字節
force() 將channel中的數據強制寫到磁盤上
scatter和gather:分散和聚合
scatter:將channel中的數據讀取到多個buffer中,按byffer數組的順序依次填充buffer
gather:將多個buffer中的數據寫到同一個channel中
1 public class TestScatterGather { 2 public static void main(String[] args) throws IOException { 3 RandomAccessFile raf = new RandomAccessFile("c:/test-nio.txt", "rw"); 4 FileChannel channel = raf.getChannel(); 5 6 ByteBuffer header = ByteBuffer.allocate(10); 7 ByteBuffer body = ByteBuffer.allocate(200); 8 9 ByteBuffer[] bufferArr = {header,body}; 10 long x = channel.read(bufferArr); 11 12 header.flip(); 13 while (header.hasRemaining()){ 14 System.out.print((char)header.get()); 15 } 16 header.clear(); 17 18 System.out.println("============"); 19 20 body.flip(); 21 while (body.hasRemaining()){ 22 System.out.print((char)body.get()); 23 } 24 body.clear(); 25 } 26 }
FileChannel的transferFrom/transferTo方法可用於複製文件,以下示例:
1 public class TestTransfer { 2 3 public static void main(String[] args) throws IOException { 4 Long s = System.currentTimeMillis(); 5 RandomAccessFile fromFile = new RandomAccessFile("C:\\迅雷下載\\linux-lite-3.6-32bit.iso", "rw"); 6 FileChannel fromChannel = fromFile.getChannel(); 7 8 RandomAccessFile toFile = new RandomAccessFile("C:\\迅雷下載\\linux-lite-3.6-32bit-bak.iso", "rw"); 9 FileChannel toChannel = toFile.getChannel(); 10 11 fromChannel.transferTo(0, fromChannel.size(), toChannel); 12 //或者 13 //toChannel.transferFrom(fromChannel, 0, fromChannel.size()); 14 15 fromChannel.close(); 16 toChannel.close(); 17 18 System.out.println("文件複製消耗時間:"+(System.currentTimeMillis()-s)); 19 } 20 }
Selector在socket的用法示例
public class TestSelector { public static void main(String[] args) throws IOException { int port = 9999; ServerSocketChannel channel = ServerSocketChannel.open(); channel.bind(new InetSocketAddress(port)); channel.configureBlocking(false); Selector selector = Selector.open(); channel.register(selector, SelectionKey.OP_ACCEPT); while (true){ System.out.println("開始監聽..."); int selNum = selector.select(); System.out.println("selnum: "+selNum); if(selNum == 0){ continue; } Set<SelectionKey> keySet = selector.selectedKeys(); Iterator<SelectionKey> iterator = keySet.iterator(); while (iterator.hasNext()){ SelectionKey selectionKey = iterator.next(); if(selectionKey.isConnectable()){ System.out.println("connectable"); } if(selectionKey.isAcceptable()){ System.out.println("acceptable"); } if(selectionKey.isReadable()){ System.out.println("readable"); } if(selectionKey.isWritable()){ System.out.println("writable"); } } } } }