初識 Java NIO

1、初識nio

  在 JDK 1. 4 中 新 加入 了 NIO( New Input/ Output) 類, 引入了一種基於通道和緩衝區的 I/O 方式,它可使用 Native 函數庫直接分配堆外內存,而後經過一個存儲在 Java 堆的 DirectByteBuffer 對象做爲這塊內存的引用進行操做,避免了在 Java 堆和 Native 堆中來回複製數據。安全

  NIO 是一種同步非阻塞的 IO 模型。同步是指線程不斷輪詢 IO 事件是否就緒,非阻塞是指線程在等待 IO 的時候,能夠同時作其餘任務。同步的核心就是 Selector,Selector 代替了線程自己輪詢 IO 事件,避免了阻塞同時減小了沒必要要的線程消耗;非阻塞的核心就是通道和緩衝區,當 IO 事件就緒時,能夠經過寫道緩衝區,保證 IO 的成功,而無需線程阻塞式地等待。服務器

 

2、NIO中的重要概念

  一、緩衝區(buffer)

  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。

  三、選擇器(selector)

  通道和緩衝區的機制,使得線程無需阻塞地等待IO事件的就緒,可是老是要有人來監管這些IO事件。這個工做就交給了selector來完成,這就是所謂的同步。要使用Selector,得向Selector註冊Channel,而後調用它的select()方法。這個方法會一直阻塞到某個註冊的通道有事件就緒,這就是所說的輪詢。一旦這個方法返回,線程就能夠處理這些事件。

  通道向選擇器註冊時,須要指定感興趣的事件,選擇器支持如下事件:

   SelectionKey.OP_CONNECT

   SelectionKey.OP_ACCEPT

   SelectionKey.OP_READ

   SelectionKey.OP_WRITE

 

3、小實例

  使用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:因本人能力有限,若有誤還請諒解;

相關文章
相關標籤/搜索