网站首页 > 博客文章 正文
前言
上篇【从入门到放弃-Java】并发编程-NIO-Channel中我们学习到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则设置为-1 public 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则设置为-1 public 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的核心知识点就掌握了。
作者:aloof_
猜你喜欢
- 2024-09-14 Java 并发基础之并发编程通识,掌握高效编程的关键
- 2024-09-14 死磕「并发编程」100天,全靠阿里大牛的这份最全「高并发套餐」
- 2024-09-14 Java高并发编程详解:深入理解并发核心库,文字可复制,高清PDF
- 2024-09-14 Java并发不止于编程更是艺术,阿里技术专家编写257页PDF,给我啃
- 2024-09-14 Java并发编程应该掌握的并发工具类,快来看看你掌握了哪些?
- 2024-09-14 spring源码解析、并发编程实战实践等深度进阶电子文档分享
- 2024-09-14 干货推荐|Java并发编程核心概念一览,面试必备
- 2024-09-14 高并发编程系列:全面剖析Java并发编程之AQS的核心实现
- 2024-09-14 JAVA并发编程(Java并发编程实战(中文版)pdf)
- 2024-09-14 "深度解析Java并发编程:线程实战篇,一文掌握多线程高效
你 发表评论:
欢迎- 最近发表
-
- 解决Win10打开软件提示“服务器没有及时响应或控制请求”
- 用友UAP解开非结构化大数据处理与分析的密码
- Windows 25H2 修复底层崩溃(windows修复环境)
- 开源技巧:使用Docker实现交互式任务
- Win11 任意版本 直接安装 Android 应用 教程完整版
- Windows Terminal已成为Windows 11操作系统的默认终端
- 那些好莱坞电影中炫酷的特效 原来是这么拍的
- Windows 10 Build 21327.1010发布:测试服务管道更新
- Made in China: The story behind the label
- PowerShell登录远程Linux系统(powershell开启远程桌面)
- 标签列表
-
- ifneq (61)
- 字符串长度在线 (61)
- googlecloud (64)
- flutterrun (59)
- 系统设计图 (58)
- powershellfor (73)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- qcombobox样式表 (68)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)