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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章