关于NIO 中Buffer中的3个重要的状态属性含义:position,limit,capacity
查看Buffer源码:
<p> A buffer's <i>capacity</i> is the number of elements it contains. The * capacity of a buffer is never negative and never changes. </p>capacity 就是容量大小,不能是负数并且是 不能改变的。
IntBuffer buf = IntBuffer.allocate(10); 分配10个小大容量
<p> A buffer's <i>limit</i> is the index of the first element that should * not be read or written. A buffer's limit is never negative and is never * greater than its capacity. </p>
limit :无法再去读或者写的第一个元素的索引。不会超过capacity
<p> A buffer's <i>position</i> is the index of the next element to be * read or written. A buffer's position is never negative and is never * greater than its limit. </p>position:下一个将要被读或者写的元素的索引,不会超过limit
下面画图来解释一下相互的关系:比如我们IntBuffer.allocate(6) 分配6个元素 初始化的时候的位置,
如果读或写2个数据 position的位置 指向下一个 位置
调用flip方法 把position 指向第0个位置 将limit指向原来 position的位置
- //创建指定长度的缓冲区
- IntBuffer buf = IntBuffer.allocate(6);
- buf.put(13);// position位置:0 - > 1
- buf.put(21);// position位置:1 - > 2
- buf.put(35);// position位置:2 - > 3
- buf.put(35);// position位置:3 - > 4
- //把位置复位为0,也就是position位置:4 - > 0
- buf.flip();
- System.out.println("使用flip复位:" + buf);
- System.out.println("容量为: " + buf.capacity()); //容量一旦初始化后不允许改变(warp方法包裹数组除外)
- System.out.println("限制为: " + buf.limit()); //由于只装载了三个元素,所以可读取或者操作的元素为4 则limit=4
<h2> Marking and resetting </h2> * * <p> A buffer's <i>mark</i> is the index to which its position will be reset * when the {@link #reset reset} method is invoked. The mark is not always * defined, but when it is defined it is never negative and is never greater * than the position. If the mark is defined then it is discarded when the * position or the limit is adjusted to a value smaller than the mark. If the * mark is not defined then invoking the {@link #reset reset} method causes an * {@link InvalidMarkException} to be thrown. *
mark 与rest 与IO中的类似
mark必须rest 搭配使用否则 将抛出异常
<h2> Invariants </h2> * * <p> The following invariant holds for the mark, position, limit, and * capacity values: * * <blockquote> * <tt>0</tt> <tt><=</tt> * <i>mark</i> <tt><=</tt> * <i>position</i> <tt><=</tt> * <i>limit</i> <tt><=</tt> * <i>capacity</i> * </blockquote>0<= mark <=position <= limit <=capacity
* <p> A newly-created buffer always has a position of zero and a mark that is * undefined. The initial limit may be zero, or it may be some other value * that depends upon the type of the buffer and the manner in which it is * constructed. Each element of a newly-allocated buffer is initialized * to zero.
新创建的buffer的position 总是0 mark 是未定义的,初始的limit 可能是0
<ul> * * <li><p> {@link #clear} makes a buffer ready for a new sequence of * channel-read or relative <i>put</i> operations: It sets the limit to the * capacity and the position to zero. </p></li> * * <li><p> {@link #flip} makes a buffer ready for a new sequence of * channel-write or relative <i>get</i> operations: It sets the limit to the * current position and then sets the position to zero. </p></li> * * <li><p> {@link #rewind} makes a buffer ready for re-reading the data that * it already contains: It leaves the limit unchanged and sets the position * to zero. </p></li> * * </ul>
clear 方法的操作 是将 buffer 恢复初始化的状态 将Limit 赋值成capacity 将position赋值为0
flip方法 把position 指向第0个位置 将limit指向原来 position的位置
rewind 方法 limit 不变 position 变为0
<h2> Read-only buffers </h2> * * <p> Every buffer is readable, but not every buffer is writable. The * mutation methods of each buffer class are specified as <i>optional * operations</i> that will throw a {@link ReadOnlyBufferException} when * invoked upon a read-only buffer. A read-only buffer does not allow its * content to be changed, but its mark, position, and limit values are mutable. * Whether or not a buffer is read-only may be determined by invoking its * {@link #isReadOnly isReadOnly} method.每个buffer是只读,但不是每个buffer是可写的,一个只读buffer不允许内容改变,但是mark,position,limit 都可以改变
* <h2> Thread safety </h2> * * <p> Buffers are not safe for use by multiple concurrent threads. If a * buffer is to be used by more than one thread then access to the buffer * should be controlled by appropriate synchronization.buffer 是非线程安全的 需要自己实现同步
java NIO 内存分配:1.堆上内存分配 (生成对象)2.堆外内存分配(不由jvm控制 由操作系统控制 也就是零拷贝)
来一个 栗子:
package nio;
import java.nio.IntBuffer;
import java.util.Random;
public class NioTest2 {
public static void main(String[] args) {
IntBuffer intBuffer = IntBuffer.allocate(10);
for (int i = 0; i <5 ; i++) {
int rand = new Random().nextInt(20);
intBuffer.put(rand);
}
System.out.println("before flip limit "+ intBuffer.limit());
intBuffer.flip();
System.out.println("after flip limit "+ intBuffer.limit());
while (intBuffer.hasRemaining()){
System.out.println("position "+ intBuffer.position());
System.out.println("limit "+ intBuffer.limit());
System.out.println("capacity "+ intBuffer.capacity());
System.out.println(intBuffer.get());
}
}
}
before flip limit 10
after flip limit 5
position 0
limit 5
capacity 10
5
position 1
limit 5
capacity 10
14
position 2
limit 5
capacity 10
4
position 3
limit 5
capacity 10
15
position 4
limit 5
capacity 10
6
我们分析一下intBuffer 首先IntBuffer是继承Buffer
public abstract class IntBuffer extends Buffer implements Comparable<IntBuffer>可以看到 allocate 方法 是返回HeapIntBuffer
/** * Allocates a new int buffer. * * <p> The new buffer's position will be zero, its limit will be its * capacity, its mark will be undefined, and each of its elements will be * initialized to zero. It will have a {@link #array backing array}, * and its {@link #arrayOffset array offset} will be zero. * * @param capacity * The new buffer's capacity, in ints * * @return The new int buffer * * @throws IllegalArgumentException * If the <tt>capacity</tt> is a negative integer */ public static IntBuffer allocate(int capacity) { if (capacity < 0) throw new IllegalArgumentException(); return new HeapIntBuffer(capacity, capacity); }
再看HeapBuffer 又是继承IntBuffer
class HeapIntBuffer extends IntBuffer { // For speed these fields are actually declared in X-Buffer; // these declarations are here as documentation /* protected final int[] hb; protected final int offset; */ HeapIntBuffer(int cap, int lim) { // package-private super(-1, 0, lim, cap, new int[cap], 0); /* hb = new int[cap]; offset = 0; */ }
HeapBuffer 通过父类的方法 再看IntBuffer 可以看到其实 IntBuffer数据是通过int[] hb 数组存储的 同时又调用了父类Buffer offset 是偏移量 我们并没有使用 通常是0
IntBuffer(int mark, int pos, int lim, int cap, // package-private int[] hb, int offset) { super(mark, pos, lim, cap); this.hb = hb; this.offset = offset; }
这时 capacity 设置为10 limit 为 10 positon 为0 这就说明 allocate 方法 将Limit 赋值成capacity 将position赋值为0
Buffer(int mark, int pos, int lim, int cap) { // package-private if (cap < 0) throw new IllegalArgumentException("Negative capacity: " + cap); this.capacity = cap; limit(lim); position(pos); if (mark >= 0) { if (mark > pos) throw new IllegalArgumentException("mark > position: (" + mark + " > " + pos + ")"); this.mark = mark; } }我们再看put方法
/** * Relative <i>put</i> method <i>(optional operation)</i>. * * <p> Writes the given int into this buffer at the current * position, and then increments the position. </p> * * @param i * The int to be written * * @return This buffer * * @throws BufferOverflowException * If this buffer's current position is not smaller than its limit * * @throws ReadOnlyBufferException * If this buffer is read-only */ public abstract IntBuffer put(int i);
因为 IntBuffer put 方法是抽象方法 其实就是将数据保存到数据里, 我们查看他的子类 HeapIntBuffer hb[] 就是保存数据的数组 上面有提到它是父类里的成员变量
public IntBuffer put(int x) { hb[ix(nextPutIndex())] = x; return this; }
我们看nextPutIndex 方法 就是将position ++ 位置移动到下个位置
final int nextPutIndex() { // package-private if (position >= limit) throw new BufferOverflowException(); return position++; }再看ix 方法 由于offset偏移量为0 就是当前position
protected int ix(int i) { return i + offset; }
我们在看flip()方法 :把position 指向第0个位置 将limit指向原来 position的位置
/** * 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; }
Buffer的基本用法
使用Buffer读写数据一般遵循以下四个步骤:
- 写入数据到Buffer
- 调用
flip()
方法 - 从Buffer中读取数据
- 调用
clear()
方法或者compact()
方法
当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。
一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
package nio;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NioTest4 {
public static void main(String[] args) throws IOException{
FileInputStream inputStream = new FileInputStream("input.txt");
FileOutputStream outputStream = new FileOutputStream("ouput.txt");
FileChannel inputStreamChannel = inputStream.getChannel();
FileChannel outputStreamChannel =outputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true){
buffer.clear();
int read =inputStreamChannel.read(buffer);
System.out.println("read : "+read );
if(-1 == read){
break;
}
buffer.flip();
outputStreamChannel.write(buffer);
}
inputStreamChannel.close();
outputStreamChannel.close();
}
}
/** * Clears this buffer. The position is set to zero, the limit is set to * the capacity, and the mark is discarded. * * <p> Invoke this method before using a sequence of channel-read or * <i>put</i> operations to fill this buffer. For example: * * <blockquote><pre> * buf.clear(); // Prepare buffer for reading * in.read(buf); // Read data</pre></blockquote> * * <p> This method does not actually erase the data in the buffer, but it * is named as if it did because it will most often be used in situations * in which that might as well be the case. </p> * * @return This buffer */ public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; }