【NIO总结】—NIO中的缓冲区

来源:互联网 发布:旅行商问题算法 编辑:程序博客网 时间:2024/06/06 11:43

        NIO中的缓冲区是一个用于特定基本数据类型的容器。在java.io包中定义,所有缓冲区都是Buffer抽象类的子类。Buffer主要用于和NIO通道进行交互,数据可以从通道读入缓冲区,也可以从缓冲区写入到通道中。Buffer就像一个数组,可以保存多个相同类型的数据。


缓冲区的类型


根据数据类型的不同,缓冲区的类型分为以下几类,其中不包括boolean对应的Buffer:

        ByteBuffer:字节缓冲区

        CharBuffer:字符缓冲区

        DoubleBuffer:双精浮点缓冲区

        FloatBuffer:单精浮点缓冲区

        IntBuffer:整型缓冲区

        LongBuffer:长整型缓冲区

        ShortBuffer:短整型缓冲区


这些不同的Buffer类都采用相似的方法操作数据,只不过操作的数据类型不同而已。例如:

        获取缓冲区(以字节缓冲区为例):ByteBuffer buffer = ByteBuffer.allocate(int capacity)或ByteBuffer buffer = ByteBuffer.allocateDirect(int capacity)。


        存入数据(以字节缓冲区为例):buffer.put("a".getBytes()),提供单个字节存入或以字节数组的形式存入。


        取出数据(以字节缓冲区为例):buffer.get(),提供获取单个字节的操作,也提供取出字节数组的形式。


        清空缓冲区(以字节缓冲区为例):buffer.clear(),清空缓冲区并返回对缓冲区的引用,清空后数据不会丢失,只不过被标记为“被遗忘”状态,通过get还是可以获取到。


        设置可读状态(以字节缓冲区为例):buffer.flip(),将缓冲区的界限设置为当前位置,并将当前位置设置为0,使得buffer设置为可读状态,此时可将buffer中的数据写入到channel中。


        重复可读(以字节缓冲区为例):buffer.rewind(),将buffer设置为重复可读,也就是将当前位置设置为0,并取消设置的mark。


            


核心概念

 

        容量(capacity):表示Buffer的最大容量,缓冲区容量不能为负,并且创建后不可更改。


        限制(limit):第一个不能读取或写入的数据的位置,即位于limit后面的数据不可读写。缓冲区的限制不能为负,也不能大于容量。


        位置(position):下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制。


        标记(mark)与重置(reset):标记是一个索引,通过Buffer中的mark()方法制定一个特定的position,之后可以调用reset()方法将position恢复到当前mark所在的位置。


        它们四个索引的值关系为:0 <= mark <= position <= limit <= capacity。

       


        当Buffer创建时,设置容量为10,此时的capacity=10,limit=10,position=0,当向Buffer中存入数据后,对于字节缓冲区来说,每存入一个字节,position就会向后移动1,如果存入5个字节,此时position的位置便是5,因为索引下标是从0开始的。当buffer调用flip()方法切换到读模式后,会将limit置为position,将position置为0,此时就可以从buffer中读取数据。随着数据不断读取,position不断后移,直至移动到limit的位置,表示数据读取完全。如果读取数据时要求不从第0个位置开始读,而是从第2个位置开始,此时就要调用mark()方法将position标记到第2个位置,这样再次读取的时候就会从mark标记的位置开始读,调用reset()方法可将position设置到mark的位置。rewind()方法可以设置重复读,一次读取完成后,调用rewind()方法,将position置为0,再次开始读取,类似于flip()。下面的方法可以测试上面的核心概念以及常用的Buffer的方法:


@Testpublic void testCoreConception(){    //1、创建一个Buffer    ByteBuffer buffer = ByteBuffer.allocate(10);    System.out.println(buffer.position());    System.out.println(buffer.limit());    System.out.println(buffer.capacity());    //2、向Buffer中加入5个字符    buffer.put("a".getBytes());    buffer.put("b".getBytes());    buffer.put("c".getBytes());    buffer.put("d".getBytes());    buffer.put("e".getBytes());    System.out.println("\n");    System.out.println(buffer.position());    System.out.println(buffer.limit());    System.out.println(buffer.capacity());    //3、执行flip()方法后    buffer.flip();    System.out.println("\n");    System.out.println(buffer.position());    System.out.println(buffer.limit());    System.out.println(buffer.capacity());    //4、从Buffer中get数据之后    byte data = buffer.get();    System.out.println("\n");    System.out.println(buffer.position());    System.out.println(buffer.limit());    System.out.println(buffer.capacity());    //5、测试rewind()    buffer.rewind();    System.out.println("\n");    System.out.println(buffer.position());    System.out.println(buffer.limit());    System.out.println(buffer.capacity());    //6、测试mark()和reset()    byte data0 = buffer.get();    buffer.mark();    byte data1 = buffer.get();    buffer.reset();    System.out.println("\n");    System.out.println(buffer.position());    System.out.println(buffer.limit());    System.out.println(buffer.capacity());    //7、测试clear()    buffer.clear();    System.out.println("\n");    System.out.println(buffer.position());    System.out.println(buffer.limit());    System.out.println(buffer.capacity());    byte data2 = buffer.get();    System.out.println((char)data2);}


直接和非直接缓冲区


        缓冲区的创建方式有两种,allocate(int capacity)和allocationDirect(int capacity),两种方式都是创建缓冲区,前面的一种是创建非直接缓冲区,后面的方法是创建直接缓冲区。


        非直接缓冲区:非直接缓冲区是由allocate()方法创建的,创建后存在于JVM内存中。此时在内核地址空间和用户地址空间之间存在着数据copy的过程,降低了数据传输的效率。


             


        直接缓冲区:直接缓冲区是由allocateDirect(int capacity)方法创建的,也可以通过FileChannel的map()方法将文件区域直接映射到内存中来创建。直接缓冲区是占用物理内存的,在物理内存中创建映射文件,从而取消了内核地址空间和用户地址空间之间的数据copy过程,提高了效率。


                  


        直接缓冲区和非直接缓冲区可以通过isDirect()方法来判断,非直接缓冲区占用JVM内存,缓冲区的分配和取消都是建立在JVM的基础上的,不会影响物理内存。直接缓冲区将映射文件映射在屋里内存中,它的分配和取消通常需要更高的成本,如果数据量较大,可能会占用内存过多导致电脑卡顿。所以,如果需要频繁创建或取消的缓冲区,最好使用非直接缓冲区,而相对稳定的缓冲区可以使用直接缓冲区。


@Testpublic void testDirectBuffer(){    ByteBuffer buffer = ByteBuffer.allocate(10);    ByteBuffer buffer1 = ByteBuffer.allocateDirect(10);    System.out.println(buffer.isDirect());    System.out.println(buffer1.isDirect());}