NIO(二)—— Buffer缓冲区
转载文章: 转载自并发编程网 – ifeve.com
Buffer缓冲区
管道
Channel
将数据写入到缓冲区Buffer
,管道Channel
从缓冲区Buffer
读取数据
基本使用
- 写数据到
Buffer
- WtoR(写模式切换为读模式)
.flip()
- 从
Buffer
读出数据 - 清除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有两种方式:
- 从Channel写到Buffer的例子
int bytesRead = inChannel.read(buf); //read into buffer
- 通过put方法写Buffer的例子:
buf.put(127);
put方法有很多版本,允许你以不同的方式把数据写入到Buffer中。例如, 写到一个指定的位置,或者把一个字节数组写入到Buffer。
flip()
flip()
方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。
换句话说,position现在用于标记读的位置,limit表示之前写进了多少个byte、char等 ,即现在能读取多少个byte、char等。
读取数据
从Buffer中读取数据
从Buffer中读取数据有两种方式:
- 从Buffer读取数据到Channel的例子:
//read from buffer into channel. int bytesWritten = inChannel.write(buf);
- 使用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解码器里解决半包问题用到