LongAddr

目录

1. 引言

2. AtomicInteger的局限性

3. AtomicInteger与LongAdder 的性能差异

4.LongAdder 的结构

LongAddr架构

Striped64中重要的属性

 Striped64中一些变量或者方法的定义

Cell类

5. 分散热点的原理

具体流程图

6. 在实际项目中的应用

7. 总结

1. 引言

在这一部分,可以简要介绍并发编程中的挑战,以及为什么传统的累加方式可能在高并发情况下表现不佳。引出LongAddr作为一种解决方案的背景。

LongAddr属于原子操作增强类,在传统的多线程编程中,我们可能会使用 synchronized 关键字或者 ReentrantLock 来保证对共享变量的原子性操作。然而,这些方法在高度并发的场景下可能会引起性能瓶颈。原子类中的AtomicInteger可以解决这个问题,但同时又出现了LongAddr来提供了一种更高效的并发累加方案。

2. AtomicInteger的局限性

  • 竞争热点

当多个线程同时竞争修改同一个 AtomicInteger实例时,会发生竞争热点。所有的线程都试图通过 CAS 操作来更新同一变量,这可能导致竞争激烈,降低性能。

3. AtomicInteger与LongAdder 的性能差异

需求:热点商品点赞计算器,点赞数加加统计,不要求实时精确;

代码:使用50个线程,每个线程100W次,总点赞数出来。

class ClickNumber
{int number = 0;public synchronized void add_Synchronized(){number++;}AtomicInteger atomicInteger = new AtomicInteger();public void add_AtomicInteger(){atomicInteger.incrementAndGet();}AtomicLong atomicLong = new AtomicLong();public void add_AtomicLong(){atomicLong.incrementAndGet();}LongAdder longAdder = new LongAdder();public void add_LongAdder(){longAdder.increment();//longAdder.sum();}LongAccumulator longAccumulator = new LongAccumulator((x,y) -> x+y,0);public void add_LongAccumulator(){longAccumulator.accumulate(1);}}/****  50个线程,每个线程100W次,总点赞数出来*/
public class LongAdderCalcDemo
{public static final int SIZE_THREAD = 50;public static final int _1W = 10000;public static void main(String[] args) throws InterruptedException{ClickNumber clickNumber = new ClickNumber();long startTime;long endTime;CountDownLatch countDownLatch1 = new CountDownLatch(SIZE_THREAD);CountDownLatch countDownLatch2 = new CountDownLatch(SIZE_THREAD);CountDownLatch countDownLatch3 = new CountDownLatch(SIZE_THREAD);CountDownLatch countDownLatch4 = new CountDownLatch(SIZE_THREAD);CountDownLatch countDownLatch5 = new CountDownLatch(SIZE_THREAD);//========================startTime = System.currentTimeMillis();for (int i = 1; i <=SIZE_THREAD; i++) {new Thread(() -> {try{for (int j = 1; j <=100 * _1W; j++) {clickNumber.add_Synchronized();}}catch (Exception e){e.printStackTrace();}finally {countDownLatch1.countDown();}},String.valueOf(i)).start();}countDownLatch1.await();endTime = System.currentTimeMillis();System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t add_Synchronized"+"\t"+clickNumber.number);startTime = System.currentTimeMillis();for (int i = 1; i <=SIZE_THREAD; i++) {new Thread(() -> {try{for (int j = 1; j <=100 * _1W; j++) {clickNumber.add_AtomicInteger();}}catch (Exception e){e.printStackTrace();}finally {countDownLatch2.countDown();}},String.valueOf(i)).start();}countDownLatch2.await();endTime = System.currentTimeMillis();System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t add_AtomicInteger"+"\t"+clickNumber.atomicInteger.get());startTime = System.currentTimeMillis();for (int i = 1; i <=SIZE_THREAD; i++) {new Thread(() -> {try{for (int j = 1; j <=100 * _1W; j++) {clickNumber.add_AtomicLong();}}catch (Exception e){e.printStackTrace();}finally {countDownLatch3.countDown();}},String.valueOf(i)).start();}countDownLatch3.await();endTime = System.currentTimeMillis();System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t add_AtomicLong"+"\t"+clickNumber.atomicLong.get());startTime = System.currentTimeMillis();for (int i = 1; i <=SIZE_THREAD; i++) {new Thread(() -> {try{for (int j = 1; j <=100 * _1W; j++) {clickNumber.add_LongAdder();}}catch (Exception e){e.printStackTrace();}finally {countDownLatch4.countDown();}},String.valueOf(i)).start();}countDownLatch4.await();endTime = System.currentTimeMillis();System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t add_LongAdder"+"\t"+clickNumber.longAdder.longValue());startTime = System.currentTimeMillis();for (int i = 1; i <=SIZE_THREAD; i++) {new Thread(() -> {try{for (int j = 1; j <=100 * _1W; j++) {clickNumber.add_LongAccumulator();}}catch (Exception e){e.printStackTrace();}finally {countDownLatch5.countDown();}},String.valueOf(i)).start();}countDownLatch5.await();endTime = System.currentTimeMillis();System.out.println("----costTime: "+(endTime - startTime) +" 毫秒"+"\t add_LongAccumulator"+"\t"+clickNumber.longAccumulator.longValue());}
}
----costTime: 895 毫秒	 add_Synchronized	50000000
----costTime: 450 毫秒	 add_AtomicInteger	50000000
----costTime: 445 毫秒	 add_AtomicLong	50000000
----costTime: 41 毫秒	 add_LongAdder	50000000
----costTime: 41 毫秒	 add_LongAccumulator	50000000

        通过结果能看出在duoLongAddr的性能要比synchronized锁和AtomicInteger要好。

4.LongAdder 的结构

LongAddr架构

       

        从上图可以看出LongAdder是Striped64的子类。

Striped64中重要的属性

/** Number of CPUS, to place bound on table size        CPU数量,即cells数组的最大长度 */
static final int NCPU = Runtime.getRuntime().availableProcessors();/*** Table of cells. When non-null, size is a power of 2.
cells数组,为2的幂,2,4,8,16.....,方便以后位运算*/
transient volatile Cell[] cells;/**基础value值,当并发较低时,只累加该值主要用于没有竞争的情况,通过CAS更新。* Base value, used mainly when there is no contention, but also as* a fallback during table initialization races. Updated via CAS.*/
transient volatile long base;/**创建或者扩容Cells数组时使用的自旋锁变量调整单元格大小(扩容),创建单元格时使用的锁。* Spinlock (locked via CAS) used when resizing and/or creating Cells. */
transient volatile int cellsBusy;

 Striped64中一些变量或者方法的定义

  • base:类似于AtomicLong中全局的value值。在没有竞争情况下数据直接累加到base上,或者cells扩容时,也需要将数据写入到base上
  • collide:表示扩容意向,false一定不会扩容,true可能会扩容。
  • cellsBusy:初始化cells或者扩容cells需要获取锁,0:表示无锁状态1:表示其他线程已经持有了锁
  • casCellsBusy():通过CAS操作修改cellsBusy的值,CAS成功代表获取锁,返回true
  • NCPU:当前计算机CPU数量,CelI数组扩容时会使用到. getProbe():获取当前线程的hash值
  • advanceProbe():重置当前线程的hash值

Cell类

Cell类是 java.util.concurrent.atomic 下 Striped64 的一个内部类

5. 分散热点的原理

        LongAdder的基本思路就是分散热点,将value值分散到一个Cell数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。
 
sum()会将所有Cell数组中的value和base累加作为返回值,核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降级更新热点。

 

Value = Base +

总结:内部有一个base变量,一个Cell[]数组。

base变量:非竞态条件下,直接累加到该变量上,Cell[]数组:竞态条件下,累加个各个线程自己的槽Cell[i]中。

具体流程图

6. 在实际项目中的应用

  • 热点商品点赞计算器,点赞数加加统计,不要求实时精确。
  • 一个很大的List,里面都是int类型,实现加加。

7. 总结

当需要在高并发下有较好的性能表现,且对值的精确度要求不高时,可以使用。

即当需要保证性能,不要求精度,可以使用。

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

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

相关文章

抖音外卖商品模型

目录 一、抖音外卖商品模型 二、商家运营流程 &#xff08;一&#xff09;商家入驻流程 &#xff08;二&#xff09;商品发布流程 三、推广带货流程 &#xff08;一&#xff09;短视频带货 &#xff08;二&#xff09;直播视频带货 【直播工具】 【直播流程】 直播前…

Redis--13--缓存一致性问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 缓存一致性问题1、先更新缓存&#xff0c;再更新DB方案二&#xff1a;先更新DB&#xff0c;再更新缓存方案三&#xff1a;先删缓存&#xff0c;再写数据库推荐1&…

Ubuntu中安装IDEA,并配置桌面快捷方式

1、首先自己下载linux版本的idea 这一步省略不说了 2、在/usr/local/路径下新建安装目录IDEA&#xff1a; mkdir -p /usr/local/IDEA3、执行如下命令&#xff0c;解压下载的压缩包到指定目录&#xff1a; tar -zxvf ideaIU-2022.3.3.tar.gz -C /usr/local/IDEA 注意&#x…

(四)基于高尔夫优化算法GOA求解无人机三维路径规划研究(MATLAB代码)

一、无人机模型简介&#xff1a; 单个无人机三维路径规划问题及其建模_IT猿手的博客-CSDN博客 参考文献&#xff1a; [1]胡观凯,钟建华,李永正,黎万洪.基于IPSO-GA算法的无人机三维路径规划[J].现代电子技术,2023,46(07):115-120 二、高尔夫优化算法GOA简介 高尔夫优化算法…

MediaPipe 3D姿态估计简明教程

姿势检测是更多地了解视频和图像中人体的重要一步。 我们现有的模型支持 2D 姿态估计已经有一段时间了&#xff0c;你们中的许多人可能已经尝试过。 今天&#xff0c;我们在 TF.js 姿势检测 API 中推出第一个 3D 模型。 3D 姿态估计为健身、医疗、动作捕捉等应用开辟了新的设计…

WordPress外贸站优化工具,WordPress外贸SEO优化方法

WordPress外贸站是跨国企业拓展市场、提升品牌知名度的理想选择。然而&#xff0c;如何通过SEO优化、原创文章生成以及留心站点优化的事项&#xff0c;成为众多站长关注的焦点。 SEO&#xff0c;即搜索引擎优化&#xff0c;是提高网站在搜索引擎结果中排名的关键。首先&#x…

云计算:数字时代的引擎

引言 云计算&#xff0c;作为现代信息技术领域的一项革命性创新&#xff0c;已经深刻改变了我们处理数据和应用的方式。它已经从仅仅是一个概念演变为一个全球范围内广泛应用的技术。云计算为个人、企业和政府机构提供了强大的计算能力、灵活性和可扩展性&#xff0c;同时降低…

JavaScript递归

前端面试大全JavaScript递归 &#x1f31f;经典真题 &#x1f31f;递归 &#x1f31f;真题解答 &#x1f31f;总结 &#x1f31f;经典真题 使用递归完成 1 到 100 的累加 &#x1f31f;递归 A recursive method is a method that calls itself. 递归调用是一种特殊的调…

使用pytorch从零开始实现迷你GPT

生成式建模知识回顾: [1] 生成式建模概述 [2] Transformer I&#xff0c;Transformer II [3] 变分自编码器 [4] 生成对抗网络&#xff0c;高级生成对抗网络 I&#xff0c;高级生成对抗网络 II [5] 自回归模型 [6] 归一化流模型 [7] 基于能量的模型 [8] 扩散模型 I, 扩散模型 II…

wordpress建站优化加速教程-Redis加速

这篇文章适合宝塔面板&#xff0c;在宝塔面板安装 Redis 实现网站加速&#xff08; Redis是一个高性能的key-value数据库(PHP连接redis&#xff0c;需PHP设置中安装redis扩展) &#xff09;。对在word press网站有着明显的加速效果。关于Redis具体说明请自己百度&#xff0c;…

编程中常见的技术难题有哪些?By AI

编程对于现代社会发展的重要性 编程&#xff0c;即按照特定的规则和逻辑&#xff0c;为计算机设计指令的过程&#xff0c;已经深深地融入现代社会的各个角落。它对人们的生活、工作和科技发展产生了深远的影响。 首先&#xff0c;编程改变了人们的生活方式。如今&#xff0c;…

Qt 如何操作SQLite3数据库?数据库创建和表格的增删改查?

# 前言 项目源码下载 https://gitcode.com/m0_45463480/QSQLite3/tree/main # 第一步 项目配置 平台:windows10 Qt版本:Qt 5.14.2 在.pro添加 QT += sql 需要的头文件 #include <QSqlDatabase>#include <QSqlError>#include <QSqlQuery>#include &…

Pandas进阶:拼接 concat 使用方法

1.处理索引和轴 假设我们有2个关于考试成绩的数据集。 df1 pd.DataFrame&#xff08;{ name&#xff1a;[A&#xff0c;B&#xff0c;C&#xff0c;D]&#xff0c;math&#xff1a;[60,89,82,70]&#xff0c;physics&#xff1a;[66&#xff0c; 95,83,66]&#xff0c;chemi…

springBoot整合task

springBoot整合task 文章目录 springBoot整合task开开关设置任务&#xff0c;并设置执行周期定时任务的相关配置 开开关 设置任务&#xff0c;并设置执行周期 Component public class MyBean {Scheduled(cron "0/1 * * * * ?")public void print(){System.out.prin…

C++进阶篇6---C++11新语法

目录 目录 一、统一的列表初始化 二、声明 1.auto 2.decltype 3.nullptr 三、范围for 四、STL中的变化 五、右值引用和移动语义(重点) 一、统一的列表初始化 在c11之前&#xff0c;我们能用{}初始化数组和结构体 struct Point {int x;int y; }; int main() {int a[] …

机器学习 - 导论

简单了解 机器学习关于数据集的概念 、

HCIP —— 双点重发布 + 路由策略 实验

目录 实验拓扑&#xff1a; 实验要求&#xff1a; 实验配置&#xff1a; 1.配置IP地址 2.配置动态路由协议 —— RIP 、 OSPF R1 RIP R4 OSPF R2 配置RIP、OSPF 双向重发布 R3配置RIP、OSPF 双向重发布 3.查询路由表学习情况 4.使用路由策略控制选路 R2 R3 5.检…

Linux shell编程学习笔记32:declare 命令

0 前言 在 Linux shell编程学习笔记16&#xff1a;bash中的关联数组https://blog.csdn.net/Purpleendurer/article/details/134053506?spm1001.2014.3001.5501 中&#xff0c;我们在定义关联数组时使用了declare命令。 其实&#xff0c;declare命令的功能不只是定义定义关…

排序算法介绍(三)选择排序

0. 简介 选择排序&#xff08;Selection Sort&#xff09;是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完。选择排序是不稳…

超大规模集成电路设计----学习框架(一)

本文仅供学习&#xff0c;不作任何商业用途&#xff0c;严禁转载。绝大部分资料来自----数字集成电路——电路、系统与设计(第二版)及中国科学院段成华教授PPT 超大规模集成电路设计----学习框架&#xff08;一&#xff09; 这门课在学什么&#xff1f;这门课该怎么学&#xf…