1. 程式人生 > >初識 Java NIO

初識 Java NIO

解決 font election locate 例如 堆外 finall 回復 因此

一、初識nio

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

  NIO 是一種同步非阻塞的 IO 模型。同步是指線程不斷輪詢 IO 事件是否就緒,非阻塞是指線程在等待 IO 的時候,可以同時做其他任務。同步的核心就是 Selector,Selector 代替了線程本身輪詢 IO 事件,避免了阻塞同時減少了不必要的線程消耗;非阻塞的核心就是通道和緩沖區,當 IO 事件就緒時,可以通過寫道緩沖區,保證 IO 的成功,而無需線程阻塞式地等待。

二、NIO中的重要概念

  1、緩沖區(buffer)

  NIO是基於緩沖區的IO方式。當一個鏈接建立完成後,IO的數據未必會馬上到達,為了使數據到達時能夠正確完成IO操作,在BIO(阻塞IO)中,等待IO的線程必須被阻塞,以全天候地執行IO操作。為了解決這種IO方式低效的問題,引入了緩沖區的概念,當數據到達時,可以預先被寫入緩沖區,再由緩沖區交給線程,因此線程無需阻塞地等待。

  常用沖區類型

   ByteBuffer、MappedByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer  

  緩沖區常用方法:

   allocate() - 分配一塊緩沖區  

   put() - 向緩沖區寫數據

   get() - 向緩沖區讀數據  

   filp() - 將緩沖區從寫模式切換到讀模式  

   clear() - 從讀模式切換到寫模式,不會清空數據,但後續寫數據會覆蓋原來的數據,即使有部分數據沒有讀,也會被遺忘;  

   compact() - 從讀數據切換到寫模式,數據不會被清空,會將所有未讀的數據copy到緩沖區頭部,後續寫數據不會覆蓋,而是在這些數據之後寫數據

   mark() - 對position做出標記,配合reset使用

   reset() - 將position置為標記值    

  緩沖區的一些屬性:

   capacity - 緩沖區大小,無論是讀模式還是寫模式,此屬性值不會變;

   position - 寫數據時,position表示當前寫的位置,每寫一個數據,會向下移動一個數據單元,初始為0;最大為capacity - 1;切換到讀模式時,position會被置為0,表示當前讀的位置

   limit - 寫模式下,limit 相當於capacity 表示最多可以寫多少數據,切換到讀模式時,limit 等於原先的position,表示最多可以讀多少數據。

  2、通道

  通道是 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。

  3、選擇器(selector)

  通道和緩沖區的機制,使得線程無需阻塞地等待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:因本人能力有限,如有誤還請諒解;

初識 Java NIO