NIO是基于Buffer的,通过channel将buffer中的内容发送给IO或保存从IO中读取的内容到buffer。
Buffer有以下属性:
- Capacity,表示buffer可保存的数据项大小,容量在buffer创建时指定且之后无法更改。
- Limit,从0开始的索引位置,从此索引及之后的 位置不应该被读取或写入。
- Position,从0开始的索引位置,表示下次读取或写入数据的位置。
- Mark,从0开始的索引位置,表示一个标记位,将调用reset()方法时会使用该位置。
这四个值的关系是:capacity >= limit >= position >= mark
所有的buffer都是可读的,但不一定都是可写入的,如果向只读的buffer写入数据,就会报ReadOnlyBufferException
异常,通过isReadOnly()
方法可以判断buffer是否是只读的。
buffer 不是线程安全的,如果要多线程同时访问,需要我们来保证线程同步。
每一种基本类型都有对应的Buffer子类,除了Boolean类型: ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, and ShortBuffer
.
ByteBuffer创建及读写
我们以ByteBuffer为例,以下列出buffer的一些基本操作,以及这些操作对position/limit的影响。
// 创建ByteBuffer,buffer中的值默认初始化为0
ByteBuffer buffer = ByteBuffer.allocate(7);
// 向buffer中添加四个值
buffer.put((byte) 10).put((byte) 20).put((byte) 30).put((byte) 40);
System.out.println(buffer.limit()); // 7
System.out.println(buffer.position()); // 4
System.out.println(buffer.capacity()); // 7
System.out.println(buffer.arrayOffset()); // 0
buffer.limit(5); // 设置limit为5
System.out.println(buffer.limit()); // 5
System.out.println(buffer.position()); // 4
System.out.println(buffer.capacity()); // 7
System.out.println(buffer.arrayOffset()); // 0
// 读取当前position的值,然后position++
System.out.println(buffer.get()); // 0
System.out.println(buffer.position()); // 5
buffer.position(0); // 设置position到0
System.out.println(buffer.get()); // 10
System.out.println(buffer.get()); // 20
System.out.println(buffer.position()); // 2
![](https://img.haomeiwen.com/i632093/4f9187dba262b3ac.png)
flip()
flip()方法将limit设置为position, position设置为0.
/**
* Flips this buffer. The limit is set to the current position and then
* the position is set to zero. If the mark is defined then it is
* discarded.
*
* <p> After a sequence of channel-read or <i>put</i> operations, invoke
* this method to prepare for a sequence of channel-write or relative
* <i>get</i> operations. For example:
*
* <blockquote><pre>
* buf.put(magic); // Prepend header
* in.read(buf); // Read data into rest of buffer
* buf.flip(); // Flip buffer
* out.write(buf); // Write header + data to channel</pre></blockquote>
*
* <p> This method is often used in conjunction with the {@link
* java.nio.ByteBuffer#compact compact} method when transferring data from
* one place to another. </p>
*
* @return This buffer
*/
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
执行flip()方法后,channel就可以从buffer中开始读取数据。
rewind()方法类似flip()方法,但是忽略limit的设置:
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
mark()和reset()
mark方法用来标记当前position,当执行reset时,将position恢复成上次标记的position。
// 创建ByteBuffer,buffer中的值默认初始化为0
ByteBuffer buffer = ByteBuffer.allocate(7);
// 向buffer中添加四个值
buffer.put((byte) 10).put((byte) 20).put((byte) 30).put((byte) 40);
System.out.println(buffer.position()); // 4
System.out.println(buffer.limit()); // 7
System.out.println(buffer.capacity()); // 7
// 将limit设置为4
buffer.limit(4);
// 将mark设置为1
buffer.position(1).mark().position(3);
System.out.println(buffer.get()); // 40
// reset将position设置为mark标记的位置
buffer.reset();
System.out.println(buffer.get()); // 20
下图说明上面代码导致的mark,position和limit的变化过程。
![](https://img.haomeiwen.com/i632093/de009ac099bd67c9.png)
reset()与clear()方法的区别:
reset是将positon设置为上次标记的position位置,而clear将position直接设置为0并将limit设置为capacity.
compact()
该方法将从当前position到limit这之间的数据,依次移动到buffer的开始位置。
![](https://img.haomeiwen.com/i632093/c80b1670bf26763b.png)
在将buffer中的数据写出去时,为了避免buffer中的数据没有被完全被写出,可以执行compact方法将未写出的数据移动到buffer头部,当再读数据到buffer中时,后续读入的数据就会跟在之前未写出的数据之后。
buf.clear(); // Prepare buffer for use
while (in.read(buf) >= 0 || buf.position != 0) {
buf.flip();
out.write(buf);
buf.compact(); // In case of partial write
}
网友评论