NIO之Buffer原理介绍

    技术2022-07-17  81

    可以看到java为除boolean外的每一种类型都准备了一个对应的Buffer类 当然,如果要到网络上传输必须得是byte

    字段功能mark用于标记某次读/写的位置,可用reset()回到这里position下一个读写的位置limit第一个不应该读/写的索引位置,position < limitcapacityBuffer能够存放的最大容量

    我们来看ByteBuffer,它有两个主要的子类,DirectByteBuffer和HeapByteBuffer,DirectByteBuffer使用的是直接内存,HeapByteBuffer用的是堆内存

    这里我们以HeapByteBuffer为例

    我们可以看到里面有个字节数组叫hb,它就是buffer实际上的存储实现,我们读/写的数据都要经过这个数组。offset是偏移量,一般字节为1

    Buffer数据结构

    前面提到,简单的说Buffer就是一个封装好的数组,刚初始化的时候,limit和capacity相同 position指向数组头部,mark为-1(只有手动使用过mark()方法mark才会变)。

    初始化->对Buffer写

    写就是从position索引处开始写,每写一个字节,position就+1(注意position是要写入的索引位置)

    // 对应代码 public ByteBuffer put(byte x) { checkSegment(); // 若position超出limit则抛出异常 hb[ix(nextPutIndex())] = x; return this; } // 若当前位置超出limit则抛出异常 final int nextPutIndex() { // package-private if (position >= limit) throw new BufferOverflowException(); // position加1 return position++; }

    写->读

    当需要读/写转换时,需要执行flip()方法

    public Buffer flip() { limit = position; position = 0; mark = -1; return this; }

    我们可以注意到,limit就是写的最后一个位置,而现在position为0,我们读取数据只能从索引position读到limit,这种限制保证了读取的数据不会越界

    读->写(读完了)

    若我们是在读取到limit之后才执行flip(),则直接重置就可以了,没啥好说的

    读->写(没读完)

    若我们需要在读完之前就转换读/写,就要使用compact()函数

    public ByteBuffer compact() { int pos = position(); int rem = limit() - pos; // 转移还没读完的数据 System.arraycopy(hb, ix(pos), hb, ix(0), rem); position(rem); limit(capacity()); discardMark(); return this; }

    compoact()的作用是把还未读完的数据转移到数组开头,并把position置为转移后的未读完数据的下一个索引

    其实你会发现,这几个方法,完全没用到mark,mark需要根据场景手动调用mark()函数

    简单使用示例

    参考: Buffer中读模式和写模式的切换

    Processed: 0.013, SQL: 10