博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【从入门到放弃-Java】并发编程-NIO-Buffer
阅读量:2402 次
发布时间:2019-05-10

本文共 4710 字,大约阅读时间需要 15 分钟。

前言

上篇中我们学习到channel是双向通道,数据通过channel在实体(文件、socket)和缓冲区(buffer)中可以双向传输。

本文我们就来学习下buffer

简介

buffer即缓冲区,实际上是一块内存,可以用来写入、读取数据。是一个线性的、大小有限的、顺序承载基础数据类型的内存块。

buffer有三个重要的属性:

  • capacity:缓冲池大小,是不可变的。当buffer写满时,需要先清空才能继续写入。
  • limit:是buffer中不可以被读或者写的第一个元素的位置,limit的大小永远不会超过capacity(在写模式下,limit等于capacity)
  • position:是buffer中可以被读或者写的第一个元素的位置,position的大小永远不会超过limit

除了boolean外,每一个基础数据类型都有对应的buffer。如:ByteBuffer、CharBuffer、LongBuffer等

buffer不是线程安全的,如果要在多线程中使用 需要加锁控制

接下来以ByteBuffer为例开始学习。

ByteBuffer

allocateDirect

public static ByteBuffer allocateDirect(int capacity) {    //会创建一个容量大小为capacity的DirectByteBuffer(ByteBuffer的子类)    return new DirectByteBuffer(capacity);}

allocate

public static ByteBuffer allocate(int capacity) {    if (capacity < 0)        throw createCapacityException(capacity);    //会创建一个容量大小为capacity的HeapByteBuffer(ByteBuffer的子类)    return new HeapByteBuffer(capacity, capacity);}

HeapByteBuffer和DirectByteBuffer的区别:

  • DirectByteBuffer是直接调用native方法在本机os::malloc()创建堆外内存;HeapByteBuffer是直接在jvm的堆中分配内存。
  • 当buffer中的数据和磁盘、网络等的交互都在操作系统的内核中发生时,使用DirectByteBuffer能避免从内核态->用户态->内核态的切换开销,所有的处理都在内核中进行,性能会比较好
  • 当频繁创建操作数据量比较小的buffer时,使用HeapByteBuffer在jvm堆中分配内存能抵消掉使用DirectByteBuffer带来的好处。

wrap

public static ByteBuffer wrap(byte[] array,                                    int offset, int length){    try {        return new HeapByteBuffer(array, offset, length);    } catch (IllegalArgumentException x) {        throw new IndexOutOfBoundsException();    }}public static ByteBuffer wrap(byte[] array) {        return wrap(array, 0, array.length);    }

将byte数组包装成一个ByteBuffer

读数据

  • 使用get方法从Buffer中读取数据
  • 从Buffer中读取数据到Channel即:Channel::write() (从buffer中读取数据写入到资源中,所以是write)

写数据

  • 使用put方法直接设置Buffer中的数据
  • 从Channel中读取数据到Buffer即:Channel::read() (从资源中读取数据写入到buffer中,所以是read)

position

//获取buffer中当前position的位置public final int position() {    return position;}//设置buffer的position为newPosition,注意newPosition要大于0且小于limit,如果remark大于newPosition则设置为-1public Buffer position(int newPosition) {    if (newPosition > limit | newPosition < 0)         throw createPositionException(newPosition);     position = newPosition;     if (mark > position) mark = -1;     return this;}

limit

//获取buffer中当前limit的位置public final int limit() {    return limit;}//设置buffer的limit为newLimit,注意newLimit要大于0且小于capacity。如果position大于newLimit这设置为newLimit,如果remark大于newLimit则设置为-1public Buffer limit(int newLimit) {    if (newLimit > capacity | newLimit < 0)        throw createLimitException(newLimit);    limit = newLimit;    if (position > limit) position = limit;    if (mark > limit) mark = -1;    return this;}

mark

public Buffer mark() {    //标记mark为当前position    mark = position;    return this;}

将当前位置做标记,在使用reset方法时,可以回到当前mark的位置

reset

public Buffer reset() {    int m = mark;    if (m < 0)        throw new InvalidMarkException();    //设置position为当前mark    position = m;    return this;}

回到之前设置mark的位置

clear

public Buffer clear() {    //设置position为0    position = 0;    //limit设置为capacity大小    limit = capacity;    //mark设置为-1(初始化)    mark = -1;    return this;}

读取完数据后调用clear,即将buffer逻辑上清空了,可以从0开始写入数据

flip

public Buffer flip() {    //limit设置为当前位置    limit = position;    //position设置为0    position = 0;    //mark设置为-1(初始化)    mark = -1;    return this;}

将buffer从写模式设置为读模式,limit设置为当前position的位置,即只能读取limit大小的数据

rewind

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

将position设置为0,即从头开始读取

remaining

public final int remaining() {    return limit - position;}

返回buffer中还有多少byte是未读的

hasRemaining

public final boolean hasRemaining() {    return position < limit;}

是否已读完

compact

public ByteBuffer compact() {    System.arraycopy(hb, ix(position()), hb, ix(0), remaining());    position(remaining());    limit(capacity());    discardMark();    return this;}

将position和limit直接的数据copy到byteBuffer的起始处,将已读数据清空,并将新的position设置为当前未读数据的末尾。这样能避免clear方法会将未读数据也清空的问题

slice

public ByteBuffer slice() {    return new HeapByteBufferR(hb,                                    -1,                                    0,                                    this.remaining(),                                    this.remaining(),                                    this.position() + offset);}ByteBuffer slice(int pos, int lim) {    assert (pos >= 0);    assert (pos <= lim);    int rem = lim - pos;    return new HeapByteBufferR(hb,                                    -1,                                    0,                                    rem,                                    rem,                                    pos + offset);}

新创建一个ByteBuffer,将缓存区分片,设置一个子缓冲区,实际上内存还是共享的,数据发生改变,两个缓冲区读取的数据都会是改变后的。

总结

Buffer最重要的三个属性:position、limit、capacity。牢记这三个属性的含义及读写切换时,设置值是如何变化的,Buffer的核心知识点就掌握了。

本文为云栖社区原创内容,未经允许不得转载。

转载于:https://my.oschina.net/u/1464083/blog/3070908

你可能感兴趣的文章
Debian 安全手册 第 4 章 - 安装后(转)
查看>>
Java Servlet和JSP教程(4)(转)
查看>>
xp上的shutdown(转)
查看>>
转换RM为MP3(转)
查看>>
Java中文问题详解(转)
查看>>
制作多系统安装盘(转)
查看>>
Java Servlet和JSP教程(2)(转)
查看>>
问题实录(四)(转)
查看>>
ADO数据库编程入门(转)
查看>>
跑一圈就进入xp(转)
查看>>
Java Servlet和JSP教程(8)(转)
查看>>
在Win9x/2000下配置Apache1.3.22+Tomcat4.0.1(转)
查看>>
端口大全(转)
查看>>
Java Servlet和JSP教程(10)(转)
查看>>
怎样制作恢复光盘(转)
查看>>
广州一银行偷逃个税300万职员人均补税近万元
查看>>
山西晋中6辆警车围堵太原许西收费站
查看>>
Linux下的压缩文件剖析(转)
查看>>
基础网络命令(转)
查看>>
广域网(WAN)简介(转)
查看>>