并发编程——累加器

目录

1 AtomicLong

1.1 核心功能

1.2 实现原理:

(1)基于 Unsafe 的底层操作

(2) volatile字段的内存可见性

(3)CAS 操作与 ABA 问题

1.3 性能分析

1.4 使用场景

2 LongAdder

核心设计原理

1 分段存储

2 分散更新策略

3.处理高竞争


1 AtomicLong

AtomicLong 是一个基于 CAS操作的原子类,用于在多线程环境下对 long 类型变量进行无锁的原子性操作。它通过底层的 Unsafe 类实现高效的原子更新,解决传统 synchronized 关键字带来的性能开销问题。

1.1 核心功能

AtomicLong 支持以下原子操作:

方法

功能

实现原理

add(long delta)

原子性地将变量增加 delta

CAS 操作

incrementAndGet()

原子性自增 1 并返回新值

add(1L)

decrementAndGet()

原子性自减 1 并返回新值

add(-1L)

getAndAdd(long delta)

原子性增加 delta

并返回原值

CAS 操作

getAndSet(long newValue)

原子性设置新值并返回原值

CAS 操作

compareAndSet(long expect, long update)

原子性将值从 expect

更新为 update

(如果相等)

CAS 操作

get()

获取当前值(非原子,但保证可见性)

直接读取 value 字段

1.2 实现原理:

(1)基于 Unsafe 的底层操作

AtomicLong 的所有原子方法均通过 sun.misc.Unsafe 类的底层原生指令实现,例如:

objectFieldOffset:获取long value字段在对象内存中的偏移量

getAndAddLong:通过CPU的CAS指令(如 cmpxchgq)原子性更新内存中的值。

(2) volatile字段的内存可见性

AtomicLongvalue 字段声明为 volatile ,确保一个线程的修改对其它线程可见:

(3)CAS 操作与 ABA 问题

  • CAS 操作

CAS(Compare-And-Swap)包含三个步骤:

  1. 比较:读取内存中的旧值 expect
  2. 交换:如果旧值等于 expect,则将新值 update 写入内存;否则不操作。
  3. 返回结果:返回内存中的旧值。
  • ABA 问题
  • 场景:变量从 A → B → A,此时 compareAndSet(A, C) 会错误地认为值未变,导致更新失败。
  • 解决方案
    • AtomicStampedReference:引入版本戳(stamp)标记变量的修改次数,解决 ABA 问题。
    • LongAdder:通过分散更新压力避免单一变量的频繁 CAS 冲突。

1.3 性能分析

优势

  • 无锁操作:避免线程阻塞和上下文切换,性能优于 synchronized
  • 读写高效:get() 方法是非原子的,但保证内存可见性,读取速度极快。
  • 适用场景:低到中等并发场景,如计数器、单变量累加器。

局限性

  • 高并发瓶颈:当多个线程激烈竞争同一变量时,CAS 失败次数剧增,导致自旋开销(spin-wait)。
  • ABA 问题:需额外处理或改用 AtomicStampedReference

1.4 使用场景

低竞争环境:如单机多线程的计数器、序列号生成。

需要强原子性保证:如银行账户扣款、分布式锁的版本控制。

2 LongAdder

设计目标LongAdder是Java 8引入的原子类,属于 java.util.concurrent.atomic 包,专为高并发场景下的累加操作优化。它的核心目标是解决AtomicLong 在及极高并发下的性能瓶颈——通过分散压力来减少线程间的CAS冲突。

核心设计原理

1 分段存储

  • cells数组: LongAdder维护一个动态增长的long[]cells数组,每个元素成为一个cell,用于存储部分累加值。
  • base变量:所有cell之外的累加值存储在base中,默认情况下,单个线程的增量优先base,当base发生竞争时,才会分配新的cell。

2 分散更新策略

  1. 优先更新 base(①):
    1. cells 未初始化(null),或通过 casBase 成功将 base 加上 x,直接返回。casBaseStriped64 提供的原子操作,用于更新 base
  2. 处理 cells 分段(②-④):
    1.  哈希索引计算getProbe() 返回与当前线程绑定的哈希值(通过 ThreadLocalRandom 生成),并与数组长度掩码按位与,得到目标 cell 的索引。
    2. CAS 更新 cell:若目标 cell 为空,通过 casCell 初始化为新值;否则尝试原子性增加 cell 的值。
    3. 竞争标记:若 CAS 失败(uncontended = false),表示存在线程竞争。

3.处理高竞争

调用 longAccumulate 方法,可能触发以下操作:

  • 动态扩容:若 cells 数组过小,倍增其大小并将 base 值分布到新 cell 中。
  • 批量写入:将当前线程的增量暂存到临时变量,直到找到可写入的 cell。
public void add(long x) {Cell[] as; long b, v; int m; Cell a;if ((as = cells) != null || !casBase(b = base, b + x)) { // ① 优先尝试更新 baseboolean uncontended = true;if (as == null || (m = as.length - 1) < 0 || // ② 处理未初始化或空数组(a = as[getProbe() & m]) == null ||     // ③ 计算哈希索引并获取目标 cell!(uncontended = a.cas(v = a.value, v + x))) { // ④ CAS 更新 celllongAccumulate(x, null, uncontended); // ⑤ 处理竞争,触发动态扩容}}
}

sum方法:返回base值和cells数组的总和

public long sum() {Cell[] as = cells; Cell a;long sum = base;if (as != null) {for (int i = 0; i < as.length; ++i) {if ((a = as[i]) != null)sum += a.value;}}return sum;
}

reset方法:重置变量,使总和保持为零。

此方法可能是创建新 adder 的有用替代方法,但仅在没有并发更新时有效。由于此方法本质上是 racy,因此仅当已知没有线程同时更新时,才应使用它。

public void reset() {Cell[] as = cells; Cell a;base = 0L;if (as != null) {for (int i = 0; i < as.length; ++i) {if ((a = as[i]) != null)a.value = 0L;}}
}

sumThenReset方法:返回当前总和后清零,适用于离线统计场景

实际上等效于 sum 后跟 reset。例如,在多线程计算之间的 static points 期间,此方法可能适用。如果存在与此方法并发的更新, 则不能保证 返回的值是重置之前出现的最终值。

public long sumThenReset() {Cell[] as = cells; Cell a;long sum = base;base = 0L;if (as != null) {for (int i = 0; i < as.length; ++i) {if ((a = as[i]) != null) {sum += a.value;a.value = 0L;}}}return sum;
}

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

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

相关文章

10 【HarmonyOS NEXT】 仿uv-ui组件开发之Avatar头像组件开发教程(一)

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; 目录 第一篇&#xff1a;Avatar 组件基础概念与设计1. 组件概述2. 接口设计2.1 形状类型定义2.2 尺寸类型定义2.3 组件属性接口 3. 设计原则4. 使用…

天津大学02-深度解读DeepSeek:部署、使用、安全【文末附下载链接】

大模型风险与不当用例——价值观错位 大模型与人类价值观、期望之间的不一致而导致的安全问题&#xff0c;包含&#xff1a;• 社会偏见&#xff08;Social Bias&#xff09;LLM在生成文本时强化对特定社会群体的刻板印象&#xff0c;例如将穆斯林与恐怖主义关联&#xff0c;或…

C#程序加密与解密Demo程序示例

目录 一、加密程序功能介绍 1、加密用途 2、功能 3、程序说明 4、加密过程 5、授权的注册文件保存方式 二、加密程序使用步骤 1、步骤一 ​编辑2、步骤二 3、步骤三 4、步骤四 三、核心代码说明 1、获取电脑CPU 信息 2、获取硬盘卷标号 3、机器码生成 3、 生成…

《Python实战进阶》No14: 使用Dask处理大规模数据集

文章特色&#xff1a; 包含3个核心代码块&#xff0c;覆盖延迟计算、分块策略和云原生集成4个架构图/监控面板示意图的占位说明对比表格清晰展示技术选型差异实战案例包含从数据读取到机器学习的完整流水线扩展思考部分引入最新云原生技术实践 Dask 是一个灵活的开源库&#…

React + React-intl @3.xx + TypeScript

声明&#xff1a;此篇文章使用的版本是 "react-intl": "^3.12.0"。 因为react-intl3.xx版本相较于react-intl2.xx版本差别较大&#xff0c;有些API是break change, 所以这篇文章的实现方式&#xff0c;不适用于react-intl2.xx版本。 一: 安装react-intl np…

TON基金会确认冠名赞助2025香港Web3嘉年华,并将于4月8日重磅呈现“TON生态日”

近日&#xff0c;由万向区块链实验室与HashKey Group联合推出的Web3年度盛典——2025香港Web3嘉年华正式宣布&#xff0c;TON基金会确认成为本届嘉年华的冠名赞助商&#xff0c;并将于4月8日在主会场特别举办“TON生态日”专题Side Event&#xff0c;集中展现TON生态的最新技术…

IDEA 基础配置: maven配置 | 服务窗口配置

文章目录 IDEA版本与MAVEN版本对应关系maven配置镜像源插件idea打开服务工具窗口IDEA中的一些常见问题及其解决方案IDEA版本与MAVEN版本对应关系 查找发布时间在IDEA版本之前的dea2021可以使用maven3.8以及以前的版本 比如我是idea2021.2.2 ,需要将 maven 退到 apache-maven-3.…

Mybatis详解-上

目录 Mybatis概述 快速入门 引入依赖 配置数据库参数 持久层代码 基本介绍 一个参数变量 多个参数变量 新增 将自增主键id赋值到userinfo对象里 参数重命名 删除 修改 查询 sql语句重命名的方法查询 指定结果映射关系 复用结果映射关系 驼峰转换 MyBatis的XM…

【PSIM】峰值电流如何用D类触发器

最近做实验发现很多人用D类触发器代替RS触发器进行峰值电流控制。研究了一下发现确实效果更好&#xff08;模电数电还给老师了。。&#xff09; 这篇文章研究一下PSIM中D类触发器的使用&#xff0c;以及如何实现峰值电流控制 选用的是带设置重置的D触发器&#xff0c;见下图 …

项目设计之用户注册与登录

流程 表设计 create table if not exists kanyuServer.user_db (id bigint unsigned auto_increment comment 主键primary key,phone varchar(11) not null comment 手机号码,password varchar(128) default null comment 密码&#xff0c;加密存储,user_name varchar(32) de…

贪吃蛇身匀速运动模型

通用运动模型 我们已知斜线为移动的距离 d d d&#xff0c; x x x轴总偏移量为 d x dx dx&#xff0c; y y y轴总偏移量为 d y dy dy&#xff0c;在一帧当中&#xff0c;我们也知道能走的距离为 m d md md。那么作为一般的运动模型&#xff0c;该如何确定我们进行移动的方向呢&…

项目管理工具 Maven

目录 1.Maven的概念 1.1​​​​​什么是Maven 1.2什么是依赖管理 1.3什么是项目构建 1.4Maven的应用场景 1.5为什么使用Maven 1.6Maven模型 2.初识Maven 2.1Maven安装 2.1.1安装准备 2.1.2Maven安装目录分析 2.1.3Maven的环境变量 2.2Maven的第一个项目 2.2.1按照约…

SSM架构 +Nginx+FFmpeg实现rtsp流转hls流,在前端html上实现视频播放

序言&#xff1a; 本文介绍通过SSM架构 NginxFFmpeg实现rtsp流转hls流&#xff0c;在前端html上实现视频播放功能。此方法可用于网络摄像头RTSP视频流WEB端实时播放。&#xff08;海康和大华都可以&#xff09;&#xff0c;我使用的是海康 步骤一&#xff1a;安装软件 FFmpeg…

超链接打开新页签传递参数

背景 有一个需求&#xff0c;网站1点击按钮后跳转到页面2&#xff0c;页面1和页面2的域名不同&#xff0c;并且需要传递参数A&#xff0c;再 那么最简单的就是 在url上带参数A 比如 https://xx.com?Axxx &#xff0c;从url上的queryParam上就能获取参数 但很可惜&#xff0c…

【入门级篇】保姆级教程:零基础实现DeepSeek本地部署的两种终极方案(附避坑指南)

关于DeepSeek的革命性冲击的看法 大家好,前阵子在忙着DeepSeek的一些本地化部署和一些开发工具,工作流的工作,最近刚好有空,借助零散时间给大家分享下DeepSeek的一些应用,为此我写了一篇专栏,从浅入深的介绍DeepSeek的一些技术,应用,以及DeepSeek的论文分析。 首先我们…

nginx简单命令启动,关闭等

启动命令 #启动nginx start nginx重启命令 比如修改了配置文件&#xff0c;用这个命令重启生效 #重启nginx nginx -s reload3&#xff0c;查看端口占用 #查看端口占用 netstat -aon4&#xff0c;关闭nginx 如果使用cmd命令窗口启动nginx&#xff0c; 关闭cmd窗口是不能…

强化学习(赵世钰版)-学习笔记(2.状态值与贝尔曼方程)

这是本课程的第二章&#xff0c;讲述状态值与贝尔曼&#xff08;Bellman&#xff09;方程的相关概念。 回报值&#xff08;Return&#xff09;的重要性&#xff1a;计算回报值可以用于评估各策略的优劣&#xff0c;可以量化分析。 回报值的计算方法&#xff1a;设Vi为某策略下起…

STM32——定时器

定时器有 捕获脉冲宽度、计算PWM占空比、输出PWM波形以及编码器计数等各种功能。 定时器又能分为 基本定时器&#xff1a;TIM6和TIM7通用定时器&#xff1a;TIM2、TIM3、TIM4和TIM5高级定时器&#xff1a;TIM1、TIM8 一、计数和分频 每当一个方波信号的上升沿经过寄存器&…

链表双指针经典习题

链表双指针经典习题 链表的分解删除排序链表中的重复元素2&#xff08;重复元素彻底删除&#xff09;方法一&#xff1a;分解链表方式二&#xff1a;快慢指针递归解法 链表的合并丑数2有序矩阵中第k小的元素查找和最小的k对数字两数相加两数相加2 回文单链表回文链表 迭代和递归…

2025年主流原型工具测评:墨刀、Axure、Figma、Sketch

2025年主流原型工具测评&#xff1a;墨刀、Axure、Figma、Sketch 要说2025年国内产品经理使用的主流原型设计工具&#xff0c;当然是墨刀、Axure、Figma和Sketch了&#xff0c;但是很多刚入行的产品经理不了解自己适合哪些工具&#xff0c;本文将从核心优势、局限短板、协作能…