NIO Buffer中各重要状态属性的含义与关系以及源码解析(一)

关于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的位置



  1.    //创建指定长度的缓冲区  
  2.         IntBuffer buf = IntBuffer.allocate(6);  
  3.         buf.put(13);// position位置:0 - > 1  
  4.         buf.put(21);// position位置:1 - > 2  
  5.         buf.put(35);// position位置:2 - > 3  
  6. buf.put(35);// position位置:3 - > 4  
  7.         //把位置复位为0,也就是position位置:4 - > 0  
  8.         buf.flip();  
  9.         System.out.println("使用flip复位:" + buf);  
  10.         System.out.println("容量为: " + buf.capacity());   //容量一旦初始化后不允许改变(warp方法包裹数组除外)  
  11.         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>&lt;=</tt>
*     <i>mark</i> <tt>&lt;=</tt>
*     <i>position</i> <tt>&lt;=</tt>
*     <i>limit</i> <tt>&lt;=</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&nbsp;&nbsp;<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读写数据一般遵循以下四个步骤:

  1. 写入数据到Buffer
  2. 调用flip()方法
  3. 从Buffer中读取数据
  4. 调用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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值