当前位置: 首页 > news >正文

多线程篇(并发相关类- 原子操作类)(持续更新迭代)

目录

前言

一、原子变量操作类(AtomicLong为例)

1. 前言

2. 实例

二、JDK 8新增的原子操作类LongAdder

三、LongAccumulator类原理探究


前言

JUC包提供了一系列的原子性操作类,这些类都是使用非阻塞算法CAS实现的,相比使用锁实现原子性操作这在性能上有很大提高。

由于原子性操作类的原理都大致相同,这里讲解最简单的AtomicLong类的实现原理以及JDK 8中新增的LongAdder和LongAccumulator

类的原理。有了这些基础,再去理解其他原子性操作类的实现就不会感到困难了。

一、原子变量操作类(AtomicLong为例)

1. 前言

JUC 并发包中包含有 AtomicInteger、AtomicLong 和 AtomicBoolean 等原子性操作类它们的原理类似,我们主要讲解 AtomicLong

类。

AtomicLong 是原子性递增或者递减类,其内部使用 Unsafe 来实现。

通过Unsafe.getUnsafe()方法获取到 Unsafe 类的实例,这里你可能会有疑问,为何能通过 Unsafe.getUnsafe()方法获取到 Unsafe 类的

实例?

其实这是因为 AtomicLong类也是在 rt.jar 包下面的,AtomicLong 类就是通过 BootStarp 类加载器进行加载的。

在没有原子类的情况下,实现计数器需要使用一定的同步措施,比如使用 synchronized 关键字等,但是这些都是阻塞算法,对性能有

一定损耗,而原子操作类都使用 CAS 非阻塞算法,性能更好。

但是在高并发情况下AtomicLong 还会存在性能问题。

JDK 8 提供了一个在高并发下性能更好的 LongAdder 类。

2. 实例

AtomicLong是原子性递增或者递减类,其内部使用Unsafe来实现,我们看下面的代码:

  • 代码(1)通过Unsafe.getUnsafe()方法获取到Unsafe类的实例,这里你可能会有疑问,为何能通过Unsafe.getUnsafe()方法获取到Unsafe类的实例?其实这是因为AtomicLong类也是在rt.jar包下面的,AtomicLong类就是通过BootStarp类加载器进行加载的。
  • 代码(5)中的value被声明为volatile的,这是为了在多线程下保证内存可见性,value是具体存放计数的变量。
  • 代码(2)(4)获取value变量在AtomicLong类中的偏移量。

下面重点看下AtomicLong中的主要函数:

递增和递减操作代码:

在如上代码内部都是通过调用Unsafe的getAndAddLong方法来实现操作,这个函数是个原子性操作,这里第一个参数是AtomicLong实

例的引用,第二个参数是value变量在AtomicLong中的偏移值,第三个参数是要设置的第二个变量的值。

boolean compareAndSet(long expect, long update)方法:

在内部调用了unsafe.compareAndSwapLong方法。

如果原子变量中的value值等于expect,则使用update值更新该值并返回true,否则返回false。

示例代码:多线程使用AtomicLong统计0的个数。

public class Atomic {// (10) 创建Long型原子计数器private static final AtomicLong atomicLong = new AtomicLong();// (11) 创建数据源private static final Integer[] arrayOne = new Integer[]{0, 1, 2, 3, 0, 5, 6, 0, 56, 0};private static final Integer[] arrayTwo = new Integer[]{10, 1, 2, 3, 0, 5, 6, 0, 56, 0};public static void main(String[] args) throws InterruptedException {//(12)线程one统计数组arrayOne中0的个数Thread threadOne = new Thread(() -> {int size = arrayOne.length;for (Integer integer : arrayOne) {if (integer == 0) {atomicLong.incrementAndGet();}}});//(13)线程two统计数组arrayTwo中0的个数Thread threadTwo = new Thread(() -> {int size = arrayTwo.length;for (Integer integer : arrayTwo) {if (integer == 0) {atomicLong.incrementAndGet();}}});// (14) 启动子线程threadOne.start();threadTwo.start();// (15) 等待线程执行完毕threadOne.join();threadTwo.join();System.out.println("arrayOne & arrayTwo 0 的个数是: " + atomicLong.get());}
}

运行结果:

如上代码中的两个线程各自统计自己所持数据中0的个数,每当找到一个0就会调用AtomicLong的原子性递增方法。

在没有原子类的情况下,实现计数器需要使用一定的同步措施,比如使用synchronized关键字等,但是这些都是阻塞算法,对性能有一定

损耗,而本章介绍的这些原子操作类都使用CAS非阻塞算法,性能更好。

但是在高并发情况下AtomicLong还会存在性能问题。

JDK 8提供了一个在高并发下性能更好的LongAdder类,下面我们来讲解这个类。

二、JDK 8新增的原子操作类LongAdder

AtomicLong通过CAS提供了非阻塞的原子性操作,相比使用阻塞算法的同步器来说它的性能已经很好了,但是JDK开发组并不满足于此。

使用AtomicLong时,在高并发下大量线程会同时去竞争更新同一个原子变量,但是由于同时只有一个线程的CAS操作会成功,这就造成

了大量线程竞争失败后,会通过无限循环不断进行自旋尝试CAS的操作,而这会白白浪费CPU资源。

因此JDK 8新增了一个原子性递增或者递减类LongAdder用来克服在高并发下使用AtomicLong的缺点。既然AtomicLong的性能瓶颈是由

于过多线程同时去竞争一个变量的更新而产生的,那么如果把一个变量分解为多个变量,让同样多的线程去竞争多个资源,是不是就解决

了性能问题?是的,LongAdder就是这个思路。下面通过图来理解两者设计的不同之处,如图所示。

使用LongAdder时,则是在内部维护多个Cell变量,每个Cell里面有一个初始值为0的long型变量,这样,在同等并发量的情况下,争夺单

个变量更新操作的线程量会减少,这变相地减少了争夺共享资源的并发量。另外,多个线程在争夺同一个Cell原子变量时如果失败了,它

并不是在当前Cell变量上一直自旋CAS重试,而是尝试在其他Cell的变量上进行CAS尝试,这个改变增加了当前线程重试CAS成功的可能

性。最后,在获取LongAdder当前值时,是把所有Cell变量的value值累加后再加上base返回的。

该类通过内部cells数组分担了高并发下多线程同时对一个原子变量进行更新时的竞争量,让多个线程可以同时对cells数组里面的元素进行

并行的更新操作。

另外,数组元素Cell使用@sun.misc.Contended注解进行修饰,这避免了cells数组内多个原子变量被放入同一个缓存行,也就是避免了

伪共享,这对性能也是一个提升。

三、LongAccumulator类原理探究

LongAdder类是LongAccumulator的一个特例,LongAccumulator比LongAdder的功能更强大。

例如下面的构造函数,其中accumulatorFunction是一个双目运算器接口,其根据输入的两个参数返回一个计算值,

identity则是LongAccumulator累加器的初始值。

上面提到,LongAdder其实是LongAccumulator的一个特例,调用LongAdder就相当于使用下面的方式调用LongAccumulator:

LongAccumulator相比于LongAdder,可以为累加器提供非0的初始值,后者只能提供默认的0值。另外,前者还可以指定累加规则,

比如不进行累加而进行相乘,只需要在构造LongAccumulator时传入自定义的双目运算器即可,后者则内置累加的规则。

相关文章:

多线程篇(并发相关类- 原子操作类)(持续更新迭代)

目录 前言 一、原子变量操作类(AtomicLong为例) 1. 前言 2. 实例 二、JDK 8新增的原子操作类LongAdder 三、LongAccumulator类原理探究 前言 JUC包提供了一系列的原子性操作类,这些类都是使用非阻塞算法CAS实现的,相比使用…...

数学建模常用工具总结

数学建模常用工具总结 绘图篇pythonMATLABLIVEGAP CHARTSApache EChartsBioLadderHiplot Pro 生物医学可视化平台Graph EditorRAWGraphs 2.0ExcalidrawPPT绘图 配色篇Color SpaceAdobe Color 素材篇手绘素材插画网iconfont-阿里巴巴矢量图标库下面四个都是实物风格的素材&#…...

【Redis】为什么选择 Redis 做缓存?

近期文章: 【Redis】Redis 底层的数据结构(结合源码) 【MySQL】索引底层的数据结构 BTree 在系统开发中,我们经常会使用 Redis 作为缓存系统,但你知道为什么要使用缓存吗?为什么是使用 Redis 作为缓存呢&…...

Flutter 开发常用第三方库总结

Flutter 开发常用第三方库总结 常用库 常用库 屏幕适配flutter_screenutil加载 svg flutter_svg状态管理 flutter_bloc(bloc、equatable)、provider视频播放器chewie图片缓存cached_network_image网络请求 dio设备信息查询device_info_plus文件路径 pat…...

OpenCV中的颜色映射函数applyColorMap的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 OpenCV 中应用类似于 GNU Octave 或 MATLAB 中的颜色映射,虽然 OpenCV 中的颜色映射类型与 GNU Octave 或 MATLAB 中的颜色映射类型名…...

Ubuntu22.04安装colmap

首先上这里查看自己电脑GPU的CMAKE_CUDA_ARCHITECTURES 终端输入以下内容安装预先的前置依赖 sudo apt-get install \git cmake ninja-build build-essential \libboost-program-options-dev libboost-filesystem-dev \libboost-graph-dev libboost-system-dev libboost-tes…...

认识GO语言中的nil,零值与空结构体

go语言的初学者,特别是java开发者新学习go语言,对于一些和java类似但是又有差异的概念很容易混淆,比如说go中的零值,nil 和 空结构体。本文就来详细探讨一下go中这些特殊概念的含义和实际场景中的应用: 零值 零值&…...

Node.js sqlite3:Statement对象详解

在Node.js的sqlite3库中,Statement对象是一个非常重要的概念。它代表了一个预编译的SQL语句,可以多次执行以提高性能。通过使用Statement对象,你可以避免重复解析和编译SQL语句的开销,特别是在需要频繁执行相同SQL语句的情况下。本…...

ELK学习笔记——如何给Kibana新增用户和角色

Kibana新增用户和角色 首先用超管账号登录上Kibana,按照下面步骤操作 1、创建角色 按图操作 2、创建用户 按图操作 3、给用户分配角色 至此,角色和用户绑定成功; 最后,可以退出管理员账号,登录这个新…...

Minikube Install Kubernetes v1.18.1

文章目录 简介安装工具配置代理运行集群检查集群加入rancher 简介 模拟客户环境,测试 kubernetes v1.18.x 是否可以被 rancher v2.9.1 纳管。 安装工具 docker 安装Install and Set Up kubectl on Linux 安装 minikube 配置代理 docker proxylinux proxy 运行…...

重修设计模式-创建型-工厂模式

重修设计模式-创建型-工厂模式 一、概述 工厂模式(Factory Pattern)是设计模式中非常基础且常用的一种模式,主要目的是通过封装对象的创建过程,从而实现代码的解耦和灵活性的提升。 工厂模式的核心思想 封装对象的创建&#x…...

使用Cskin时候 遇到按钮有默认阴影问题解决

使用Cskin时候 遇到按钮有默认阴影 设置 DrawType 属性就可以了...

121.rk3399 uboot(2017.09) 源码分析1(2024-09-05)

参考源码 : uboot(2017.09) 硬件平台:rk3399 辅助工具:linux虚拟机,sourceinsight4,文件浏览器(可以使用samba访问),ultraeidt(查看bin文件比较方便) 说明&#xff1a…...

【图论】虚树 - 模板总结

适用于解决一棵树中只需要用到少部分点的时候,将需要用到的点提出来单独建一棵树 /********************* 虚树 *********************/ struct edge {int to, next;int val; };struct Virtual_Tree {int n; // 点数int dfn[N]; // dfs序int dep[N]; // 深度int fa…...

[C#学习笔记]注释

官方文档&#xff1a;Documentation comments - C# language specification | Microsoft Learn 一、常用标记总结 1.1 将文本设置为代码风格的字体&#xff1a;<c> 1.2 源代码或程序输出:<code> 1.3 异常指示:<exception> 1.4 段落 <para> 1.5 换行&…...

c# checkbox的text文字放到右边

checkbox的text文字放到右边 实现方法如下图 特此记录 anlog 2024年9月2日...

【node.js】基础之修改文件

node.js 基础(一) node.js是什么&#xff1f; 上面这句话的意思就是&#xff1a;Node.js 是一个开源的&#xff0c;跨平台的javascript运行环境。通俗的说就是一个应用程序或者说是一个软件&#xff0c;可以运行javascript。 Node.js的作用&#xff1a; 开发服务器应用。 将数…...

Notepad++回车不自动补全

问题 使用Notepad时&#xff0c;按回车经常自动补全&#xff0c;但我们希望回车进行换行&#xff0c;而不是自动补全&#xff0c;而且自动补全使用Tab进行补全足够了。下文介绍设置方法。 设置方法 打开Notepad&#xff0c;进入设置 - 首选项 - 自动完成&#xff0c;在插入选…...

CSS线性渐变拼接,一个完整的渐变容器(div),要拆分成多个渐变容器(div),并且保持渐变效果一致

1 需求 一个有渐变背景的div&#xff0c;需要替换成多个渐变背景div拼接&#xff0c;渐变效果需要保持一致&#xff08;不通过一个大的div渐变&#xff0c;其他子的div绝对定位其上并且背景透明来解决&#xff09; 2 分析 主要工作&#xff1a; 计算完整div背景线性渐变时的…...

【60天备战软考高级系统架构设计师——第十天:软件设计与架构综合练习】

经过前十天的学习&#xff0c;我们已经了解了软件工程生命周期模型、需求分析与管理方法&#xff0c;以及软件设计与架构的核心内容。为了巩固这些知识点&#xff0c;今天我们将进行一个综合练习。 前十天学习内容回顾 第1-3天&#xff1a;软件工程概述 学习了软件生命周期模…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...