天天看点

Java NIO源码分析之Buffer

        Java NIO的主要读写处理逻辑就是将数据从通道读入缓冲区,从缓冲区写入到通道中。而这个数据缓冲区的基类就是Buffer。而Buffer本质上就是一块可读写数据的内存,其提供了一些方法,方便外部调用者访问这块内存进行数据读写操作。

        使用Buffer读写数据的主要步骤,大体如下:

        1、将数据写入Buffer;

        2、调用flip()方法,将读写模式由写模式切换成读模式;

        3、从Buffer中读取数据;

        4、调用clear()方法或者compact()方法清空缓冲区,完成数据读操作。

        Buffer中三个十分重要的属性

        1、capacity

        代表了Buffer的最大容量,标识出Buffer中最多可以存储的capacity个byte、long、char等类型的数据;

        2、position

        代表了缓冲区Buffer当前待写入或待读取位置,初始值为0,当一个byte、long、char等数据被写入或者被读取后,position自动移动到下一个可写入位置,其最大值为capacity – 1,实际上它受limit限制。一般情况下,Buffer从写模式切换到读模式或者从读模式切换到写模式,position均会被重置为0;

        3、limit

        表示可写入或可读取数据的限制。在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据,limit等于Buffer的capacity。在读模式下,limit表示你最多能读到多少数据,当切换Buffer到读模式时,limit会被设置成写模式下的position值,也就是说你能读到之前写入的所有数据。

        Buffer中,获取这些属性的方法分别是:capacity()、position()、limit()方法,而position、limit也有对应方法进行设置,下面的源码分析我们会分别介绍。

        Byffer中,一个不可变的定律是:mark <= position <= limit <= capacity。

        初始情况下:

        mark = -1

        position = 0

        limit = capacity - 1

        capacity = m 

        写入n个数据时:

        mark = -1

        position = n

        capacity = m(m >= n)

        flip()切换写模式至读模式时:

        limit = position(也就是n)

        读取k个数据时

        position = k

        Buffer有两种模式:读模式和写模式。这两种模式的切换是是通过以下方法完成的:

        1、写模式到读模式:flip()

        2、读模式到写模式:clear()或compact()(compact()方法为ByteBuffer中的抽象方法)

        Buffer源码分析:

        flip()方法

        flip()实际上是翻转数据缓冲区Buffer,将其由写模式转换成读模式。大体逻辑如下:

        1、将limit设置为当前位置position,标识出读模式下最大可读取数据限制为之前已写入数据;

        2、当前位置position设置为0,标识可读取数据的起始位置;

        3、标记mark设置为-1,即无效;

        4、返回当前Buffer实例。

        clear()方法

        clear()方法看上去像是清空缓冲区buffer,但是这个方法并不实际删除缓冲区中的数据,而是将其由读模式转换成写模式。它的主要逻辑是:

        1、当前位置position设置为0,又可以从0开始写入数据;

        2、读写限制limit设置为buffer的最大容量capacity,即我们可以写入数据至完全填充整个缓冲区buffer;

        position()方法

        position()方法用于获取当前缓冲区buffer的当前可写或可读位置position。

        position(int newPosition)方法

        rewind()方法

        一般情况下,flip只能被调用一次,如果是数据需要被重新读入,怎么办?这时rewind()方法就被派上用场了,其代码如下: