nio buffer用法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nio buffer用法相关的知识,希望对你有一定的参考价值。

参考技术A NIO的Buffer(缓冲区)本质上是一个内存块,既可以写入数据,也可以从中读取数据。NIO的Buffer类,是一个抽象类,位于java.nio包中,其内部是一个内存块(数组)。NIO的Buffer与普通的内存块(Java数组)不同的是:NIOBuffer对象,提供了一组更加有效的方法,用来进行写入和读取的交替访问。需要强调的是:Buffer类是一个非线程安全类.

1. Buffer类Buffer类是一个抽象类,对应于Java的主要数据类型,在NIO中有8种缓冲区类,分别如下 :ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer、MappedByteBuffer。前7种Buffer类型,覆盖了能在IO中传输的所有的Java基本数据类型。第8种类型MappedByteBuffer是专门用于内存映射的一种ByteBuffer类型。实际上,使用最多的还是ByteBuffer二进制字节缓冲区类型,后面会看到。
2.capacity属性Buffer类的capacity属性,表示内部容量的大小。
       一旦写入的对象数量超过了capacity容量,缓冲区就满了,不能再写入了。Buffer类的capacity属性一旦初始化,就不能再改变。原因是什么呢?Buffer类的对象在初始化时,会按照capacity分配内部的内存。在内存分配好之后,它的大小当然就不能改变了。再强调一下,capacity容量不是指内存块byte[]数组的字节的数量。capacity容量指的是写入的数据对象的数量。前面讲到,Buffer类是一个抽象类,Java不能直接用来新建对象。使用的时候,必须使用Buffer的某个子类,例如使用DoubleBuffer,则写入的数据是double类型,如果其capacity是100,那么我们最多可以写入100个double数据。
3.position属性Buffer类的position属性,表示当前的位置。
        position属性与缓冲区的读写模式有关。在不同的模式下,position属性的值是不同的。当缓冲区进行读写的模式改变时,position会进行调整。
    在写入模式下,position的值变化规则如下:
(1)在刚进入到写模式时,position值为0,表示当前的写入位置为从头开始。
(2)每当一个数据写到缓冲区之后,position会向后移动到下一个可写的位置。
(3)初始的position值为0,最大可写值position为limit–1。当position值达到limit时,缓冲区就已经无空间可写了。
在读模式下,position的值变化规则如下:
(1)当缓冲区刚开始进入到读模式时,position会被重置为0。
(2)当从缓冲区读取时,也是从position位置开始读。读取数据后,position向前移动到下一个可读的位置。(3)position最大的值为最大可读上限limit,当position达到limit时,表明缓冲区已经无数据可读。起点在哪里呢?当新建一个缓冲区时,缓冲区处于写入模式,这时是可以写数据的。数据写入后,如果要从缓冲区读取数据,这就要进行模式的切换,可以使用(即调用)flip翻转方法,将缓冲区变成读取模式。在这个flip翻转过程中,position会进行非常巨大的调整,具体的规则是:position由原来的写入位置,变成新的可读位置,也就是0,表示可以从头开始读。flip翻转的另外一半工作,就是要调整limit属性。
3.limit属性Buffer类的limit属性,表示读写的最大上限。
        limit属性,也与缓冲区的读写模式有关。在不同的模式下,limit的值的含义是不同的。在写模式下,limit属性值的含义为可以写入的数据最大上限。在刚进入到写模式时,limit的值会被设置成缓冲区的capacity容量值,表示可以一直将缓冲区的容量写满。在读模式下,limit的值含义为最多能从缓冲区中读取到多少数据。
一般来说,是先写入再读取。当缓冲区写入完成后,就可以开始从Buffer读取数据,可以使用flip翻转方法,这时,limit的值也会进行非常大的调整。具体如何调整呢?将写模式下的position值,设置成读模式下的limit值,也就是说,将之前写入的最大数量,作为可以读取的上限值。在flip翻转时,属性的调整,将涉及position、limit两个属性,这种调整比较微妙,不是太好理解,举一个简单例子:首先,创建缓冲区。刚开始,缓冲区处于写模式。position为0,limit为最大容量。然后,向缓冲区写数据。每写入一个数据,position向后面移动一个位置,也就是position的值加1。假定写入了5个数,当写入完成后,position的值为5。这时,使用(即调用)flip方法,将缓冲区切换到读模式。limit的值,先会被设置成写模式时的position值。这里新的limit是5,表示可以读取的最大上限是5个数。同时,新的position会被重置为0,表示可以从0开始读。
       除了前面的3个属性,第4个属性mark(标记)比较简单。就是相当一个暂存属性,暂时保存position的值,方便后面的重复使用position值。下面用一个表格总结一下Buffer类的4个重要属性,参见表3-1。Buffer四个重要属性的取值说明

4.buffer的重要方法
     allocate()创建缓冲区,put()写入到缓冲区,flip()翻转,Buffer.clear()从读取模式转换为写入模式

5.buffer的使用基本步骤
总体来说,使用JavaNIOBuffer类的基本步骤如下:
(1)使用创建子类实例对象的allocate()方法,创建一个Buffer类的实例对象。
(2)调用put方法,将数据写入到缓冲区中。
(3)写入完成后,在开始读取数据前,调用Buffer.flip()方法,将缓冲区转换为读模式。
(4)调用get方法,从缓冲区中读取数据。
(5)读取完成后,调用Buffer.clear()或Buffer.compact()方法,将缓冲区转换为写入模式。

本资料收集于Netty、Redis、Zookeeper高并发实战,希望大家购买正版图书

java nio2

Buffer的基本用法 

使用Buffer读写数据一般遵循以下四个步骤: 

  • 写入数据到Buffer
  • 调用flip()方法
  • 从Buffer中读取数据
  • 调用clear()方法或者compact()方法


当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。

一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。 

 

Buffer的类型 

Java NIO 有以下Buffer类型: 

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


如你所见,这些Buffer类型代表了不同的数据类型。换句话说,就是可以通过char,short,int,long,float 或 double类型来操作缓冲区中的字节。 

MappedByteBuffer 有些特别,在涉及它的专门章节中再讲。 

Buffer的分配 

要想获得一个Buffer对象首先要进行分配。 每一个Buffer类都有一个allocate方法。下面是一个分配48字节capacity的ByteBuffer的例子。 

Java代码 
  1. ByteBuffer buf = ByteBuffer.allocate(48);  



这是分配一个可存储1024个字符的CharBuffer: 

Java代码 
  1. CharBuffer buf = CharBuffer.allocate(1024);  



向Buffer中写数据 

写数据到Buffer有两种方式: 

  • 从Channel写到Buffer。
  • 通过Buffer的put()方法写到Buffer里。


从Channel写到Buffer的例子 

Java代码 
  1. int bytesRead = inChannel.read(buf); //read into buffer.  



通过put方法写Buffer的例子: 

Java代码 
  1. buf.put(127);  



put方法有很多版本,允许你以不同的方式把数据写入到Buffer中。例如, 写到一个指定的位置,或者把一个字节数组写入到Buffer。 更多Buffer实现的细节参考JavaDoc。  

从Buffer中读取数据 

从Buffer中读取数据有两种方式: 

  • 从Buffer读取数据到Channel。
  • 使用get()方法从Buffer中读取数据。


从Buffer读取数据到Channel的例子: 

Java代码 
  1. //read from buffer into channel.  
  2. int bytesWritten = inChannel.write(buf);  



使用get()方法从Buffer中读取数据的例子 

Java代码 
  1. byte aByte = buf.get();  



get方法有很多版本,允许你以不同的方式从Buffer中读取数据。例如,从指定position读取,或者从Buffer中读取数据到字节数组。更多Buffer实现的细节参考JavaDoc。 

rewind()方法 

Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。 

clear()与compact()方法 

一旦读完Buffer中的数据,需要让Buffer准备好再次被写入。可以通过clear()或compact()方法来完成。 

如果调用的是clear()方法,position将被设回0,limit被设置成 capacity的值。换句话说,Buffer 被清空了。Buffer中的数据并未清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。 

如果Buffer中有一些未读的数据,调用clear()方法,数据将“被遗忘”,意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。 

如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先先写些数据,那么使用compact()方法。

compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。 

以上是关于nio buffer用法的主要内容,如果未能解决你的问题,请参考以下文章

NIO 之 缓冲区(Buffer)

java nio2

java NIO之buffer

Java NIO系列教程 Buffer

java-nio网络编程

Java NIO 缓冲区(Buffer)