【从入门到放弃-Java】并发编程-NIO-Buffer

前言

上篇【从入门到放弃-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为当前positionmark = position;return this;
}

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

reset

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

回到之前设置mark的位置

clear

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

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

flip

public Buffer flip() {//limit设置为当前位置limit = position;//position设置为0position = 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的核心知识点就掌握了。


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/518476.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腾讯云成立星星海实验室,聚焦云原生服务器硬件研发

近日腾讯云宣布成立“星星海实验室”&#xff0c;这是腾讯历史上首个硬件工程实验室&#xff0c;也是腾讯云面向产业互联网加速技术自研的重要战略。 星星海取名自青海省果洛藏族自治州玛多星星海&#xff0c;以水为名&#xff0c;寓意灵动与智慧。据了解&#xff0c;星星海实…

Flowable 数据库表结构 ACT_ID_INFO

二进制数据表&#xff0c;存储通用的流程定义和流程资源。&#xff08;act_ge_bytearray&#xff09; 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√ID_ID_ID_nvarchar(64)√主键IDREV_乐观锁int√乐观锁VersionUSER_ID_用户IDnvarchar(64)√TYPE_类型nvarcha…

html-网页基本信息

<!-- DOCTYPE: 告诉浏览器&#xff0c;我们要使用什么规范--> <!DOCTYPE html> <html lang"en"> <!-- head 标签代表网页头部 --> <head><!-- meta描述性标签&#xff0c;他用来描述网站的一些信息 --><!-- meat 一般用来…

【从入门到放弃-Java】并发编程-NIO-Channel

前言 上篇[【从入门到放弃-Java】并发编程-NIO使用]()简单介绍了nio的基础使用&#xff0c;本篇将深入源码分析nio中channel的实现。 简介 channel即通道&#xff0c;可以用来读、写数据&#xff0c;它是全双工的可以同时用来读写操作。这也是它与stream流的最大区别。 cha…

【IPF2020】浪潮集团执行总裁、首席科学家王恩东:智慧计算、源动新基建

CSDN记者于前方报道 众所周知计算力就是生产力&#xff0c;智慧计算改造升级了生产力三要素并最终驱动了人类社会的转型升级。具体来说&#xff0c;智慧计算将劳动者由人变成了人与人工智能的结合体&#xff0c;以此可以顺利实现指数级增长&#xff0c;将数据变成一种创新生产…

使用Spark Streaming SQL基于时间窗口进行数据统计

1.背景介绍 流式计算一个很常见的场景是基于事件时间进行处理&#xff0c;常用于检测、监控、根据时间进行统计等系统中。比如埋点日志中每条日志记录了埋点处操作的时间&#xff0c;或者业务系统中记录了用户操作时间&#xff0c;用于统计各种操作处理的频率等&#xff0c;或…

html-网页基本标签

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>基本标签学习</title> </head> <body><!-- 标题标签 --> <h1>一级标签</h1> <h2>二级标签</h2> <…

阿里AI再出神器,“你是什么垃圾”一拍便知

“干垃圾&#xff0c;还是湿垃圾&#xff1f;你是什么垃圾&#xff1f;” 相信魔都的小伙伴已经要被垃圾分类逼疯了&#xff0c;还要面临垃圾桶前&#xff0c;志愿者们的灵魂一问&#xff1a;“你是什么垃圾&#xff1f;” 更糟糕的是&#xff0c;垃圾分类&#xff0c;还要“…

Flowable 数据库表结构 ACT_ID_GROUP

用户组信息表( act_id_group ) 字段名称字段描述数据类型主键为空取值说明ID_ID_nvarchar(64)√ID_ID_ID_nvarchar(64)√主键IDREV_乐观锁int√乐观锁VersionNAME_名称nvarchar(255)√组名称TYPE_类型nvarchar(255)√类型

【IPF2020】浪潮集团高级副总裁彭震:智算中心 筑基智慧世界

【快讯】浪潮关注智算中心&#xff0c;据浪潮集团高级副总裁彭震来看主要归结为几个主要问题&#xff0c;分别是算力、数据以及互联。此外针对智算中心的分析往往不仅仅是一个中心的单一要素&#xff0c;更多是很多中心之间彼此互联的关系&#xff0c;如何解决多元融合的问题才…

数据湖正在成为新的数据仓库

像公有云数据湖和 Delta Lake 这样的平台指出了一个中央数据枢纽的趋势&#xff0c;用来支持决策和AI驱动的自动化决策。 数据仓库是否再次加入这股浪潮呢&#xff0c;或者会逐渐消亡&#xff1f; 如果你不清楚这个问题的答案也很正常。数据仓库在一方面目前仍处于热门阶段。…

html-图像标签

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>图像标签学习</title> </head> <body> <!-- img学习 src: 图片地址 必填相对地址&#xff08;推荐&#xff09; &#xff0c; …

数据装载全/存量直接装载到目标表_09

由于全/存量数据量大&#xff0c;一次性&#xff0c;为了提升加载速度&#xff0c;采用直接将数据装载到目标表 文章目录1. 修改表控制文件2. 删除表数据3. 执行加载1. 修改表控制文件 # 切换oracle su - oracle# 进入控制文件目录 cd /app/jiazai/sql_loadv1.0/config_file# …

工程师如何给女友买包?问问阿里“百事通”

阿里妹导读&#xff1a;工作那么忙&#xff0c;怎么给女朋友买包&#xff1f;是翻看包包的详情页&#xff0c;再从商品评论中去找信息吗&#xff1f;为了帮助类似的同学节省时间&#xff0c;阿里工程师们提出快速回答生成模型RAGE。你问它答&#xff0c;这个“百事通”能从整体…

如何成功构建大规模 Web 搜索引擎架构?

Web搜索引擎十分复杂&#xff0c;我们的产品是一个分布式系统&#xff0c;在性能和延迟方面有非常苛刻的要求。除此之外&#xff0c;这个系统的运营也非常昂贵&#xff0c;需要大量人力&#xff0c;当然也需要大量金钱。这篇文章将探讨我们使用的一些技术栈&#xff0c;以及我们…

html-超链接标签

一、 a标签 <!-- a标签 href: 必填&#xff0c; 表示要跳转到哪个页面 target: 表示窗口在哪里打开_blank 在新标签中打开_self 在当前网页打开 --><a href"1.我的第一个网页.html" target"_blank">点击跳转到第一个页面</a> <a …

运维编排场景系列----给实例加到SLS机器组

场景简介 我们经常会有这样的运维场景&#xff0c;扩容一批机器需要配置SLS日志&#xff0c;对于已经配置好的SLS Logstore后&#xff0c;我们只需要将机器加到机器组里。 解决方案 传统的解决方案是登录每台ecs实例并安装logtail&#xff0c;执行的命令为 wget http://log…

数据装载指定一张表或者多张表直接装载到目标表_10

数据装载指定一张表或者多张表&#xff0c;直接装载到目标表 文章目录1. 复制脚本2. 直接加载目标表1. 复制脚本 # 切换oracle su - oracle# 进入根目录 cd /app/jiazai/sql_loadv1.0# 复制脚本 cp load.sh load-one.sh# 进入script/shell目录 cd /app/jiazai/sql_loadv1.0/sc…

UI2CODE复杂背景无法识别?闲鱼工程师这样打造高准确率方案

引言: 复杂背景内容提取指的是从复杂的背景中提取出特定的内容&#xff0c;例如在图片中提取特定的文字&#xff0c;在图片中提取特定的叠加图层等等。 这是一个业界难题&#xff0c;基于传统的图像处理的方法存在准确率和召回率的问题&#xff0c;没法解决语义的问题。而主流…

万字干货:一步步教你如何在容器上构建持续部署!

作者| 倚天码农责编| 徐威龙封图| CSDN下载于视觉中国要想理解持续集成和持续部署&#xff0c;先要了解它的部分组成&#xff0c;以及各个组成部分之间的关系。下面这张图是我见过的最简洁、清晰的持续部署和集成的关系图。图源&#xff1a;sonatype持续部署如上图所示&#xf…