【并发编程】原子累加器

       📝个人主页:五敷有你      
 🔥系列专栏:并发编程
⛺️稳重求进,晒太阳

JDK8之后有专门做累加的类,效率比自己做快数倍以上

累加器性能比较

参数是方法

  • // supplier 提供者 无中生有 ()->结果
  • // function 函数 一个参数一个结果 (参数)->结果 , BiFunction (参数1,参数2)->结果
  • // consumer 消费者 一个参数没结果 (参数)->void, BiConsumer (参数1,参数2)->void
private static<T> void demo(Supplier<T> adderSupplier,Consumer<T> action){T adder=adderSupplier.get();long start=System.nanoTime();List<Thread> ts=new ArrayList<>();// 4 个线程,每人累加 50 万for(int i=0;i< 40;i++){ts.add(new Thread(()->{for(int j=0;j< 500000;j++){action.accept(adder);}}));}ts.forEach(t->t.start());ts.forEach(t->{try{t.join();}catch(InterruptedException e){e.printStackTrace();}});long end=System.nanoTime();System.out.println(adder+" cost:"+(end-start)/1000_000);
}

比较 AtomicLong 与 LongAdder

for (int i = 0; i < 5; i++) {demo(() -> new LongAdder(), adder -> adder.increment());
}
for (int i = 0; i < 5; i++) {demo(() -> new AtomicLong(), adder -> adder.getAndIncrement());
}

原子累加器 花费116ms, 自己写花费 938ms 

        性能提升的原因很简单,就是在有竞争时,设置多个累加单元,Therad-0 累加 Cell[0],而 Thread-1 累加Cell[1]... 最后将结果汇总。这样它们在累加时操作的不同的 Cell 变量,因此减少了 CAS 重试失败,从而提高性能。 

源码之LongAdder

LongAdder 是并发大师 @author Doug Lea 的作品,设计精巧

LongAdder类有几个关键域

// 累加单元数组, 懒惰初始化
transient volatile Cell[] cells;
// 基础值, 如果没有竞争, 则用 cas 累加这个域
transient volatile long base;
// 在 cells 创建或扩容时, 置为 1, 表示加锁
transient volatile int cellsBusy;

CAS锁

// 不要用于实践!!!
public class LockCas {private AtomicInteger state = new AtomicInteger(0);public void lock() {while (true) {if (state.compareAndSet(0, 1)) {break;}}}public void unlock() {log.debug("unlock...");state.set(0);}
}

 测试

LockCas lock = new LockCas();
new Thread(() -> {System.out.println("begin...");lock.lock();try {System.out.println("lock...");sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}
}).start();
new Thread(() -> {System.out.println("begin...");lock.lock();try {System.out.println("lock...");} finally {lock.unlock();}
}).start();

输出

原理之伪共享

其中 Cell 即为累加单元

得从缓存说起

缓存与内存的速度比较

因为 CPU 与 内存的速度差异很大,需要靠预读数据至缓存来提升效率。

缓存以缓存行为单位,每个缓存行对应着一块内存,一般是 64 byte(8 个 long)

缓存的加入会造成数据副本的产生,即同一份数据会缓存在不同核心的缓存行中

CPU 要保证数据的一致性,如果某个 CPU 核心更改了数据,其它 CPU 核心对应的整个缓存行必须失效

因为 Cell 是数组形式,在内存中是连续存储的,一个 Cell 为 24 字节(16 字节的对象头和 8 字节的 value),因此缓存行可以存下 2 个的 Cell 对象。这样问题来了:

  • Core-0 要修改 Cell[0]
  • Core-1 要修改 Cell[1]

无论谁修改成功,都会导致对方 Core 的缓存行失效,比如 Core-0 中 Cell[0]=6000, Cell[1]=8000 要累加Cell[0]=6001, Cell[1]=8000 ,这时会让 Core-1 的缓存行失效

@sun.misc.Contended 用来解决这个问题,它的原理是在使用此注解的对象或字段的前后各增加 128 字节大小的padding(填充),从而让 CPU 将对象预读至缓存时占用不同的缓存行,这样,不会造成对方缓存行的失效

累加主要调用下面的方法

  public void add(long x) {// as 为累加单元数组// b 为基础值// x 为累加值Cell[] as; long b, v; int m; Cell a;// 进入 if 的两个条件// 1. as 有值, 表示已经发生过竞争, 进入 if// 2. cas 给 base 累加时失败了, 表示 base 发生了竞争, 进入 ifif ((as = cells) != null || !casBase(b = base, b + x)) {// uncontended 表示 cell 没有竞争boolean uncontended = true;if (// as 还没有创建as == null || (m = as.length - 1) < 0 ||// 当前线程对应的 cell 还没有(a = as[getProbe() & m]) == null ||// cas 给当前线程的 cell 累加失败 uncontended=false ( a 为当前线程的 cell )!(uncontended = a.cas(v = a.value, v + x))) {// 进入 cell 数组创建、cell 创建的流程longAccumulate(x, null, uncontended);}}}

add 流程图

final void longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended) {int h;// 当前线程还没有对应的 cell, 需要随机生成一个 h 值用来将当前线程绑定到 cellif ((h = getProbe()) == 0) {// 初始化 probeThreadLocalRandom.current();// h 对应新的 probe 值, 用来对应 cellh = getProbe();wasUncontended = true;}// collide 为 true 表示需要扩容boolean collide = false;for (;;) {Cell[] as; Cell a; int n; long v;// 已经有了 cellsif ((as = cells) != null && (n = as.length) > 0) {// 还没有 cellif ((a = as[(n - 1) & h]) == null) {// 为 cellsBusy 加锁, 创建 cell, cell 的初始累加值为 x// 成功则 break, 否则继续 continue 循环}// 有竞争, 改变线程对应的 cell 来重试 caselse if (!wasUncontended)wasUncontended = true;// cas 尝试累加, fn 配合 LongAccumulator 不为 null, 配合 LongAdder 为 nullelse if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x))))break;// 如果 cells 长度已经超过了最大长度, 或者已经扩容, 改变线程对应的 cell 来重试 caselse if (n >= NCPU || cells != as)collide = false;// 确保 collide 为 false 进入此分支, 就不会进入下面的 else if 进行扩容了else if (!collide)collide = true;// 加锁else if (cellsBusy == 0 && casCellsBusy()) {// 加锁成功, 扩容continue;}// 改变线程对应的 cellh = advanceProbe(h);}// 还没有 cells, 尝试给 cellsBusy 加锁else if (cellsBusy == 0 && cells == as && casCellsBusy()) {// 加锁成功, 初始化 cells, 最开始长度为 2, 并填充一个 cell// 成功则 break;}// 上两种情况失败, 尝试给 base 累加else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x))))break;}

longAccumulate 流程图

每个线程刚进入 longAccumulate 时,会尝试对应一个 cell 对象(找到一个坑位)

获取最终结果通过 sum 方法

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;
}

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

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

相关文章

ElasticSearch搜索与分析引擎-Linux离线环境安装教程

目录 一、下载安装包 网盘链接: 二、安装流程及遇到的问题和解决方案 &#xff08;1&#xff09;JDK安装 &#xff08;2&#xff09;Elasticsearch安装 &#xff08;3&#xff09;Kibana安装 ​&#xff08;4&#xff09;Ik分词器安装 三、启动过程中的问题 &#xff…

zabbix监控mariadb数据库

zabbix监控mariadb数据库 1.创建监控用户及授权 [rootchang ~]# mysql -uroot -p123qqq.A MariaDB [(none)]> CREATE USER monitor% IDENTIFIED BY 123qqq.A; MariaDB [(none)]> GRANT REPLICATION CLIENT,PROCESS,SHOW DATABASES,SHOW VIEW ON *.* TO monitor%; Maria…

【复现】WordPress html5-video-player SQL 注入漏洞_39

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 在WordPress中播放各种视频文件。一个简单&#xff0c;可访问&#xff0c;易于使用和完全可定制的视频播放器&#xff0c;适用于所…

Android SELinux:保护您的移动设备安全的关键

Android SELinux&#xff1a;保护您的移动设备安全的关键 1 引言 移动设备在我们的生活中扮演着越来越重要的角色&#xff0c;我们几乎把所有重要的信息都存储在这些设备上。然而&#xff0c;随着移动应用程序的数量不断增加&#xff0c;安全性也变得越来越关键。这就是为什么…

Android14之Selinux报错:unknown type qemu_device at token (一百八十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

PyTorch使用

前言 系统环境&#xff1a;win10 使用Anaconda&#xff0c;Anaconda的安装自行百度。 conda 23.7.4 目录 前言 创建虚拟环境 1、查看当前有哪些虚拟环境 2、创建虚拟环境pytorch 3、激活及关闭pytorch虚拟环境 4、删除pytorch虚拟环境 使用yolov5测试 1、切换至yolo…

ubuntu离线安装k8s

目录 一、前期准备 二、安装前配置 三、安装docker 四、安装cri-dockerd 五、部署k8s master节点 六、整合kubectl与cri-dockerd 七、网络等插件安装 八、常见问题及解决方法 一、前期准备 ①ubuntu系统 本地已安装ubuntu系统&#xff0c;lsb_release -a命令查看版本信…

《vtk9 book》 官方web版 第3章 - 计算机图形基础 (2 / 6)

3.4 表面特性 当光线穿过空间时&#xff0c;其中一些光线会与我们的物体相交。当这种情况发生时&#xff0c;光线会与物体表面相互作用&#xff0c;产生一种颜色。部分由此产生的颜色实际上并不是由直接光线产生的&#xff0c;而是来自其他物体反射或散射的环境光。环境光照模型…

物联网与智慧景区的未来:机遇与挑战并存

随着科技的不断发展&#xff0c;物联网技术在智慧景区中的应用越来越广泛&#xff0c;为旅游业带来了巨大的变革。然而&#xff0c;在物联网与智慧景区的未来发展中&#xff0c;机遇与挑战并存。本文将探讨物联网与智慧景区面临的机遇和挑战&#xff0c;并提出应对措施&#xf…

Spring框架——主流框架

文章目录 Spring(轻量级容器框架)Spring 学习的核心内容-一图胜千言IOC 控制反转 的开发模式Spring快速入门Spring容器剖析手动开发- 简单的 Spring 基于 XML 配置的程序课堂练习 Spring 管理 Bean-IOCSpring 配置/管理 bean 介绍Bean 管理包括两方面: Bean 配置方式基于 xml 文…

网络空间内生安全数学基础(2)——编码信道数学模型

目录 &#xff08;零&#xff09;这篇博客在干什么&#xff08;一&#xff09;内生安全与香农信道编码定理&#xff08;二&#xff09;基本定义&#xff08;三&#xff09;编码信道存在定理&#xff08;三.壹&#xff09;编码信道存在第一定理&#xff08;三.贰&#xff09;编码…

[python] 过年燃放烟花

目录 新年祝福语 一、作品展示 二、作品所用资源 三、代码与资源说明 四、代码库 五、完整代码 六、总结 新年祝福语 岁月总是悄然流转&#xff0c;让人感叹时间的飞逝&#xff0c;转眼间又快到了中国传统的新年&#xff08;龙年&#xff09;。 回首过去&#xf…

LLM - Qwen-72B LoRA 训练与推理实战

目录 一.引言 二.模型简介 1.Qwen-Model 总览 2.Qwen-Chat-72B - PreTrain - Tokenizer - Base Line - SFT / RLHF 3.Qwen-72 模型架构 - Config.json - c_attn/c_proj - Attention Forward - ROPE - Qwen MLP - Qwen Block 三.QLoRA 与 Infer 实战 1.SFT 指…

JavaScript鼠标拖放(Drag and Drop)

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 拖放是现代界面不可或缺的交互方式之一。本文将介绍如何用JavaScript…

【蓝桥杯冲冲冲】 [SCOI2005] 骑士精神

蓝桥杯备赛 | 洛谷做题打卡day28 文章目录 蓝桥杯备赛 | 洛谷做题打卡day28[SCOI2005] 骑士精神题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示题解代码我的一些话[SCOI2005] 骑士精神 题目描述 输入格式 第一行有一个正整数 T T T( T ≤ 10

随机森林超参数的网格优化(机器学习的精华--调参)

随机森林超参数的网格优化&#xff08;机器学习的精华–调参&#xff09; 随机森林各个参数对算法的影响 影响力参数⭐⭐⭐⭐⭐几乎总是具有巨大影响力n_estimators&#xff08;整体学习能力&#xff09;max_depth&#xff08;粗剪枝&#xff09;max_features&#xff08;随机…

系统架构设计师考试大纲2023

一、 考试方式&#xff08;机考&#xff09; 考试采取科目连考、 分批次考试的方式&#xff0c; 连考的第一个科目作答结束交卷完成后自动进 入第二个科目&#xff0c; 第一个科目节余的时长可为第二个科目使用。 高级资格&#xff1a; 综合知识科目考试时长 150 分钟&#xff…

使用goland IDE编写go windows ui

最近突发奇想&#xff0c;想实现一款工作节奏的提示安排小闹钟。那首先解决的就是UI。本人擅长go语言。那go在windows ui的探索肯定有人做过了吧。一查还真有&#xff0c;通过知乎&#xff0c;csdn等查到目前支持最好的就是walk库了。那走起试试。 一、拷贝go代码 将官网例子…

idea中找到所有的TODO

idea中找到所有的TODO &#xff08;1&#xff09;快捷键 Alt6 &#xff08;2&#xff09;View -> Tool Windows -> TODO

kafka客户端生产者消费者kafka可视化工具(可生产和消费消息)

点击下载《kafka客户端生产者消费者kafka可视化工具&#xff08;可生产和消费消息&#xff09;》 1. 前言 因在工作中经常有用到kafka做消息的收发&#xff0c;每次调试过程中&#xff0c;经常需要查看接收的消息内容以及人为发送消息&#xff0c;从网上搜寻了一下&#xff0…