NIO詳細介紹(Buffer)1

NIO是同步非阻塞的。NIO裏面有幾個概念需要了解:緩衝區(Buffer)、選擇器(Selector)、通道(Channel)。

1.緩衝區(Buffer):

緩衝區實際上是一個容器對象,更直接的說,其實就是一個數組,在 NIO 庫中,所有數據都是用緩衝區處理的。在讀
取數據時,它是直接讀到緩衝區中的; 在寫入數據時,它也是寫入到緩衝區中的;任何時候訪問 NIO 中的數據,都
是將它放到緩衝區中。

而在面向流I/O 系統中,所有數據都是直接寫入或者直接將數據讀取到 Stream 對象中。

在NIO 中,所有的緩衝區類型都繼承於抽象類 Buffer,最常用的就是 ByteBuffer,對於 Java 中的基本類型,基本都有
一個具體 Buffer 類型與之相對應,它們之間的繼承關係如下圖所示:


 

2.Buffer的基本使用方法:

我們說過Buffer是一個特殊數組。它裏面有幾個屬性:

position:指定下一個將要被寫入或者讀取的元素索引,它的值由 get()/put()方法自動更新,在新創建一個 Buffer 對象
時,position 被初始化爲0。
limit:指定還有多少數據需要取出(在從緩衝區寫入通道時),或者還有多少空間可以放入數據(在從通道讀入緩衝區時)。

capacity:指定了可以存儲在緩衝區中的最大數據容量,實際上,它指定了底層數組的大小,或者至少是指定了准許我
們使用的底層數組的容量。

上面屬性的關係:0 <=position <= limit <=capacity。

我們寫個代碼來看下這三個屬性:

準備一個test文檔,存放的 D盤,輸入以下內容:

下面寫段代碼讀取D盤這個文件:

package com.gupaoedu.vip.netty.io.nio.buffer;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.FileChannel;

public class BufferDemoTest {
    public static void main(String[] args) throws IOException {
        //用文件io讀取d盤裏面的文件
        FileInputStream fileInput=new FileInputStream ("D://test.txt");
        //操作文件的管道
        FileChannel channel=fileInput.getChannel ();

        //新建一個10大小的緩衝區。(10個大小的byte數組)
        ByteBuffer buffer= ByteBuffer.allocate (10);
        outPrint("初始化",buffer);

        channel.read (buffer);
        outPrint("調用read方法",buffer);

        //準備操作之前,先鎖定操作範圍
        buffer.flip();
        outPrint("調用flip()", buffer);

        //判斷有沒有可讀數據
        while (buffer.remaining() > 0) {
            byte b = buffer.get();
            System.out.print(((char)b));
        }
        outPrint("調用get()", buffer);

        //可以理解爲解鎖
        buffer.clear();
        outPrint("調用clear()", buffer);

        //最後把管道關閉
        channel.close();


    }
    //獲取每個階段的buffer的三個屬性
    public static void outPrint(String step,ByteBuffer buffer){
    System.out.println (step+":");
        //容量,數組大小
        System.out.print("capacity: " + buffer.capacity() + ", ");

    //buffer裏面的position屬性(當前操作數據所在的位置,也可以叫做遊標)
        System.out.print("position: " + buffer.position() + ", ");

       //鎖定值,flip,數據操作範圍索引只能在position - limit 之間
        System.out.println("limit: " + buffer.limit());

    }
}

輸出結果:

初始化:
capacity: 10, position: 0, limit: 10
調用read方法:
capacity: 10, position: 4, limit: 10
調用flip():
capacity: 10, position: 0, limit: 4
test調用get():
capacity: 10, position: 4, limit: 4
調用clear():
capacity: 10, position: 0, limit: 10

看着代碼分析下:

初始化:capacity: 10, position: 0, limit: 10 畫圖如下:

在調用read方法,把數據讀取到緩存區裏面, 如果讀取4個自己的數據,則此時position 的值爲 4,即下一個將要被寫入的字節索引爲 4,而 limit 仍然是10,如下圖所示:

下一步把讀取的數據寫入到輸出通道中,相當於從緩衝區中讀取數據,在此之前,必須調用 flip()方法,該方法將會完
成兩件事情:

1. 把limit 設置爲當前的 position 值
2. 把position 設置爲0

因爲position 爲0,limit爲我們讀取讀取數據的最後一個位置。我們就可以用position 來讀取放在緩存區裏面的數據。

現在調用 get()方法從緩衝區中讀取數據寫入到輸出通道,這會導致position 的增加而 limit 保持不變,但 position 不
會超過limit 的值,所以在讀取我們之前寫入到緩衝區中的 4個自己之後,position 和 limit的值都爲 4,如下圖所示:

在從緩衝區中讀取數據完畢後,limit的值仍然保持在我們調用 flip()方法時的值,調用 clear()方法能夠把所有的狀態變
化設置爲初始化時的值,如下圖所示:

3.緩衝區的分配

我們看上面代碼,在創建一個緩衝區對象時,會調用靜態方法allocate()來指定緩衝區的容量,其實調用
allocate()相當於創建了一個指定大小的數組,並把它包裝爲緩衝區對象。或者我們也可以直接將一個現有的數組,包裝爲緩衝區對象,如下示例代碼所示:

public class BufferWrapTest {
    public static void main(String[] args) {
        // 分配指定大小的緩衝區  
        ByteBuffer buffer1 = ByteBuffer.allocate(10);

        // 包裝一個現有的數組  
        byte array[] = new byte[10];
        ByteBuffer buffer2 = ByteBuffer.wrap( array );
    }
}

 


 

 

 

 

發佈了8 篇原創文章 · 獲贊 4 · 訪問量 1371
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章