NIO(二)—— Buffer缓冲区

转载文章: 转载自并发编程网 – ifeve.com

Buffer缓冲区

管道Channel将数据写入到缓冲区Buffer,管道Channel从缓冲区Buffer读取数据

基本使用

  1. 写数据到Buffer
  2. WtoR(写模式切换为读模式).flip()
  3. Buffer读出数据
  4. 清除Buffer.clear() or .compact()

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

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

读取文件的例子

RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();

//为buf分配48byte的空间
ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf); // 通过管道将文件数据写入buf
while (bytesRead != -1) {

  buf.flip();  // 从写模式转换为读模式

  while(buf.hasRemaining()){
      System.out.print((char) buf.get()); // 循环读取1byte并打印
  }

  buf.clear(); //清空buf,并等待写
  bytesRead = inChannel.read(buf); // 通过管道将文件数据接入buf
}
aFile.close();

容量 Capacity

可以看出,Buffer与系统底层的缓冲区设计是十分相似的
为buffer设定固定容量Capacity,当Buffer满了,则必须清空或者读取数据才能继续写Buffer

位置 Position

可以当做指针

  • 写模式下
    position 表示当前位置,初始为0,当写入一个数据后,指针会移动到下一个可以写的Buffer单元,Position最大值为capacity - 1
  • 读模式下
    从某特定位置开始读。当从写模式切换为读模式时,position置零。当读取一个数据时,指针会移动到下一个可读的单元

范围 Limit

  • 写模式下
    limit即为Buffer的capacity
  • 读模式下
    limit表示最多能读多少数据,此时limit为写模式下position的值,即为已写数据的数量。

Buffer操作

分配空间

ByteBuffer buf = ByteBuffer.allocate(48); //分配48个byte空间

写数据

写数据到Buffer有两种方式:

  1. 从Channel写到Buffer的例子
    int bytesRead = inChannel.read(buf); //read into buffer
    
  2. 通过put方法写Buffer的例子:
    buf.put(127);
    

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

flip()

flip()方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。
换句话说,position现在用于标记读的位置,limit表示之前写进了多少个byte、char等 ,即现在能读取多少个byte、char等。

读取数据

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

  1. 从Buffer读取数据到Channel的例子:
    //read from buffer into channel.
    int bytesWritten = inChannel.write(buf);
    
  2. 使用get()方法从Buffer中读取数据的例子
    byte aByte = buf.get();
    

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准备好写数据了,但是不会覆盖未读的数据。

mark()与reset()

通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position。例如:

buffer.mark();
//call buffer.get() a couple of times, e.g. during parsing.
buffer.reset();  //set position back to mark.

这个东西在Netty解码器里解决半包问题用到

你可能感兴趣的:(NIO(二)—— Buffer缓冲区)