JAVA NIO - Buffer

NIO是基于Buffer的,通过channel将buffer中的内容发送给IO或保存从IO中读取的内容到buffer。

Buffer有以下属性:

  1. Capacity,表示buffer可保存的数据项大小,容量在buffer创建时指定且之后无法更改。
  2. Limit,从0开始的索引位置,从此索引及之后的 位置不应该被读取或写入。
  3. Position,从0开始的索引位置,表示下次读取或写入数据的位置。
  4. 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
buffer_basic.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的变化过程。

mark_reset.png

reset()与clear()方法的区别:

reset是将positon设置为上次标记的position位置,而clear将position直接设置为0并将limit设置为capacity.

compact()

该方法将从当前position到limit这之间的数据,依次移动到buffer的开始位置。

compact.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
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容

  • 在Java NIO中,Buffer用于与Channel交互。数据从Channel读入Buffer,并从Buffer...
    d3f59bfc7013阅读 269评论 0 1
  • Java NIO Buffer与Channel需要配合来使用。如你所知,数据是从channel被读取至buffer...
    kopshome阅读 326评论 0 0
  • Buffer和Channel总是成对出现,在Java NIO中Buffer用于和NIO通道进行交互,数据总是从Ch...
    zhanglbjames阅读 565评论 0 0
  • 更多 Java IO & NIO方面的文章,请参见文集《Java IO & NIO》 Java IO VS NIO...
    专职跑龙套阅读 1,046评论 1 5
  • 偶然在网上浏览到这样的一些字眼“写一个自己的RPC框架”,心中也很激愤。于是乎想从头开始,深入研究一下如何...
    私奔_1f4f阅读 309评论 0 0