深入理解并发之LongAdder、DoubleAdder的实现原理

深入理解LongAdder、DoubleAdder的实现原理

本文主要通过LongAdder和DoubleAdder的源码,讲述一下其实现原理。通过LongAdder和DoubleAdder的源码可知。两者都是继承了Striped64的类。下面我们将通过源码的形式讲述一下这三个类都做了哪些事情。

1: Striped64

​ 首先,我们看下Striped64这个类,做了哪些功能。

1.1 Cell类(内部类)
@sun.misc.Contended static final class Cell {
    
    volatile long value;
    
    Cell(long x) { 
        value = x; 
    }
    
    final boolean cas(long cmp, long val) {
        return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
    }

    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long valueOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> ak = Cell.class;
            valueOffset = UNSAFE.objectFieldOffset
                (ak.getDeclaredField("value"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

1: @sun.misc.Contended 注解作用?

​ 在前面的文章中,我们讲到缓存行锁能够代替总线锁的唯一条件:变量已经在缓存行中

问题1: 我们思考一个问题,那一个数据可以分散在同一个缓存行中吗?

​ 答案是否定的。如果一个数据分散在一个缓存行中,那么CPU加锁就没有作用了。那么 @sun.misc.Contended 注解就是将该变量数据强制刷到不同的缓存行中。

问题2: 如果能够做到强制刷到不同的缓存行中?

​ 根据缓存行的长度,变量不足长度的数据,进行强制填充。将缓存行填充完整。

问题3: 如何填充?

2: Cell类最终是为了解决什么问题?

主要是为了 降低了CAS的范围

在这里插入图片描述

如上图,线程如果CAS操作一直不成功的时候,就会导致CPU一直空转。那么我们是不是可以思考一下,将int数据进行打散,分成若干个值,分别计算,最好将值进行汇总。变成如下图形式:

在这里插入图片描述

这些,CPU的竞争降低了很多,降低了CAS的范围。采用的思想就是降低锁粒度,提供并发性能

2: Striped64类
abstract class Striped64 extends Number {
	/**
     * cell数组。当非空时,size是2的幂
     */
    transient volatile Cell[] cells;
    
    /**
     *  基础值,当没有竞争的时候使用,使用cas更新
     */
    transient volatile long base;
    
    /**
     *  cas操作base属性值
     */
    final boolean casBase(long cmp, long val) {
        return UNSAFE.compareAndSwapLong(this, BASE, cmp, val);
    }
    
    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long BASE;
    private static final long CELLSBUSY;
    private static final long PROBE;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> sk = Striped64.class;
            BASE = UNSAFE.objectFieldOffset
                (sk.getDeclaredField("base"));
            CELLSBUSY = UNSAFE.objectFieldOffset
                (sk.getDeclaredField("cellsBusy"));
            Class<?> tk = Thread.class;
            PROBE = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomProbe"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

2: LongAdder

public class LongAdder extends Striped64 implements Serializable {
   
    /**
     * Adds the given value.
     *
     * @param x the value to add
     */
    public void add(long x) {
        Cell[] as; long b, v; int m; Cell a;
        if ((as = cells) != null || !casBase(b = base, b + x)) {
            /**
             * 能够进入的两种情况
             * 1: 如果当前数组不为空,或者casBase不成功的时候,也就是说存在竞争的情况下
             * 2: 数据为空,casBas成功,不存在竞争,直接casBase
             */
            boolean uncontended = true; //是否竞争标识: 控制多个线程是否竞争,同一个cell
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null || 
                !(uncontended = a.cas(v = a.value, v + x)))//如果内部cell没有初始化,退回到原来的cas 
                // 如果cell没有初始化,则对cell进行初始化。
                longAccumulate(x, null, uncontended);
        }
    }

    /** 
     * 所有cell值进行汇总求和,额外加上base
     */
    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;
    }
}
final void longAccumulate(long x, LongBinaryOperator fn,
                          boolean wasUncontended) {
    int h;
    if ((h = getProbe()) == 0) {
        ThreadLocalRandom.current(); // force initialization
        h = getProbe();
        wasUncontended = true;
    }
    boolean collide = false;                // True if last slot nonempty
    for (;;) {
        Cell[] as; Cell a; int n; long v;
         // 如果cells数组不为null,并且数组已经存在值
        if ((as = cells) != null && (n = as.length) > 0) {
            // 当线程访问的下标没有初始化,则进行初始化
            if ((a = as[(n - 1) & h]) == null) {
                if (cellsBusy == 0) {       // Try to attach new Cell
                    Cell r = new Cell(x);   // Optimistically create
                    if (cellsBusy == 0 && casCellsBusy()) {
                        boolean created = false;
                        try {               // Recheck under lock
                            Cell[] rs; int m, j;
                            if ((rs = cells) != null &&
                                (m = rs.length) > 0 &&
                                rs[j = (m - 1) & h] == null) {
                                rs[j] = r;
                                created = true;
                            }
                        } finally {
                            cellsBusy = 0;
                        }
                        if (created)
                            break;
                        continue;           // Slot is now non-empty
                    }
                }
                collide = false;
            }//没有发生竞争,设置成true
            else if (!wasUncontended)       // CAS already known to fail
                wasUncontended = true;      // Continue after rehash
            // 再次尝试一下是否能够自旋成功    
            else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                         fn.applyAsLong(v, x))))
                break;
            else if (n >= NCPU || cells != as)
                collide = false;            // At max size or stale
            else if (!collide)
                collide = true;
               //继续找个cell,进行自旋
            else if (cellsBusy == 0 && casCellsBusy()) {
                try {
                    if (cells == as) {      //进行扩容
                        Cell[] rs = new Cell[n << 1];
                        for (int i = 0; i < n; ++i)
                            rs[i] = as[i];
                        cells = rs;
                    }
                } finally {
                    cellsBusy = 0;
                }
                collide = false;
                continue;                   // Retry with expanded table
            }
            h = advanceProbe(h);
        } //casCellsBusy() 设置一个标志位,只允许一个线程进来
        else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
            //cell数组为空,需要初始化
            boolean init = false;
            try {                           // Initialize table
                if (cells == as) {
                    Cell[] rs = new Cell[2];
                    rs[h & 1] = new Cell(x);
                    cells = rs;
                    init = true;
                }
            } finally {
                cellsBusy = 0; //释放锁
            }
            if (init)
                //如果初始化了,则退出循环
                break;
        }
        else if (casBase(v = base, ((fn == null) ? v + x : //默认累加
                                    fn.applyAsLong(v, x))))
            //只允许一个线程对cell数组 初始化。使用casBase 来控制多个线程并发初始化,使用cas操作保证只有一个线程能够成功。所以只有一个线程能够创建cell数组,其他线程失败。不能够让其他线程做无谓的自旋,
            break;  
    }
}

DoubleAdder类

通过阅读DoubleAdder的代码之后,你会发现DoubleAdder和LongAdder的代码几乎一样,所以两者的原理都是一样的,这里就不对DoubleAdder 的代码一行行注释了,大家自行阅读。

以上就是本次分享的主要的内容。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用C#(WinForm)开发触摸屏,体验感满满

用C#&#xff08;WinForm&#xff09;开发触摸屏&#xff0c;体验感满满

人工智能大模型的进化之路:探索如何让它们变得更“聪明”

一、引言 在人工智能&#xff08;AI&#xff09;领域&#xff0c;大模型凭借其强大的处理能力和广泛的应用前景&#xff0c;已经成为研究的热点。然而&#xff0c;尽管这些模型在多个领域展现出了惊人的能力&#xff0c;但它们仍然面临着理解力、泛化能力和适应性等方面的挑战…

中央空调节能的分户计费系统

中央空调节能 在建筑能耗中&#xff0c;中央空调能耗一般占到了40%---60%的比例&#xff0c;因此如何有效降低空调能耗就成为建筑节能的重中之重。 项目案例描述 山东银座购物广场&#xff1a;为集购物中心、高级酒店式公寓和办公为一体的综合性公共建筑。整体建筑共为地下3层&…

我们身边的北斗:你知道吗北斗还能帮我们演唱会抢票

当我们步入2024年&#xff0c;演唱会与音乐节的热潮依然不减。每当周末或节假日&#xff0c;各大城市的演唱会场馆总是人头攒动&#xff0c;歌声与掌声交织成一片欢乐的海洋。而在朋友圈里&#xff0c;也总能看到乐迷们晒出的演唱会现场照片和视频&#xff0c;分享着那份独特的…

智领未来,安全无忧:智能网联汽车监控大屏的守护之旅

在繁忙的都市中&#xff0c;驾驶者往往面临着诸多安全隐患。传统的驾驶辅助系统虽然能够提供一定的帮助&#xff0c;但在复杂多变的交通环境中&#xff0c;其局限性也逐渐显现。而智能网联汽车安全监控大屏&#xff0c;正是为了解决这一问题而诞生的。 山海鲸可视化大屏 大屏采…

36. 【Java教程】输入输出流

本小节将会介绍基本输入输出的 Java 标准类&#xff0c;通过本小节的学习&#xff0c;你将了解到什么是输入和输入&#xff0c;什么是流&#xff1b;输入输出流的应用场景&#xff0c;File类的使用&#xff0c;什么是文件&#xff0c;Java 提供的输入输出流相关 API 等内容。 1…

【翻译软件】CopyTranslator复制即翻译的外文辅助阅读翻译软件NO.102

使用平台&#xff1a;Windows/Linux/macOS 设置里选择翻译引擎和翻译API&#xff0c;谷歌翻译已经退出中国了&#xff0c;但还是提供了镜像地址 一、复制即翻译 只需要复制文本到剪贴板&#xff0c;就可以查看翻译结果 记得开启“自动粘贴”哦。 二、多段同时翻译 三、智能…

别再emo了,还不赶紧去考PMP,搞钱要紧~

自从疫情之后经济大不如从前&#xff0c;现在大环境都不好&#xff0c;很多公司都在裁员&#xff0c;像我朋友就在上个月被裁掉了&#xff0c;虽说拿了补偿但也不可能靠那点补偿生活的&#xff0c;所以我朋友找了很久的工作&#xff0c;但是由于大环境的缺失所以导致他的薪资直…

误删照片怎么办?恢复删除的图片,3个指南!

在我们的日常生活中&#xff0c;照片就像是我们的小秘密宝藏&#xff0c;记录着我们与亲朋好友一起嗨皮的时光&#xff0c;还有那些让我们激动不已的人生大事。可是&#xff0c;有时候我们可能会因为一时的疏忽&#xff0c;比如手滑点错了按钮&#xff0c;或者在清理手机内存时…

微软云计算Windows Azure(一)

目录 一、微软云计算平台二、微软云操作系统Windows Azure&#xff08;一&#xff09;Windows Azure概述&#xff08;二&#xff09;Windows Azure计算服务&#xff08;三&#xff09;Windows Azure存储服务&#xff08;四&#xff09;Windows Azure Connect&#xff08;五&…

2003远程桌面端口修改,修改远程桌面端口的操作

在信息技术领域&#xff0c;远程桌面端口的修改是一项至关重要的安全操作&#xff0c;尤其在运行Windows 2003操作系统的环境中。对于系统管理员而言&#xff0c;了解和掌握如何正确、有效地修改远程桌面端口&#xff0c;是确保服务器安全、防止潜在攻击的关键步骤。 首先&…

图解Mysql索引原理

概述 是什么 索引像是一本书的目录列表&#xff0c;能根据目录快速的找到具体的书本内容&#xff0c;也就是加快了数据库的查询速度索引本质是一个数据结构索引是在存储引擎层&#xff0c;而不是服务器层实现的&#xff0c;所以&#xff0c;并没有统一的索引标准&#xff0c;…

笔记:如何在pycharm中使用anaconda的虚拟环境,新建工程和更改现有工程的虚拟环境。

1.用anaconda创建虚拟环境 (base) C:\Users\Administrator>conda -V conda 24.5.0(base) C:\Users\Administrator>conda create -n appenv python Channels:- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main- defaults Platform: win-64 Collecting package m…

upload-labs-第一关和第二关

目录 第一关 思路&#xff1a; 1、上传一个php文件 2、查看源码 3、查看文件上传地址 4、BP抓包&#xff0c;修改文件后缀名 5、使用蚁剑连接 第二关 1、这一关也可以跟第一关一样的方法进行绕过 2、上传一个一句话木马文件 第一关 原理&#xff1a; 思路&#xff1a…

Vray渲染器的作用是什么?渲染100邀请码1a12

Vray是一款专业的渲染器&#xff0c;为不同领域的CG制作者提供高质量的渲染&#xff0c;它有以下几个作用。 1、Vray能创建专业的照明效果&#xff0c;渲染最逼真的画面&#xff0c;让场景生动无比。 2、Vray能模拟各种自然光和人造光源&#xff0c;如太阳光、天空光、区域光…

乡村振兴的乡村生态文明建设:加强乡村生态环境保护,推进乡村绿色发展,打造生态宜居的美丽乡村

目录 一、引言 二、乡村生态环境保护的必要性 三、加强乡村生态环境保护的措施 &#xff08;一&#xff09;完善法律法规&#xff0c;强化制度保障 &#xff08;二&#xff09;加强宣传教育&#xff0c;提高环保意识 &#xff08;三&#xff09;推广生态农业&#xff0c;…

机器视觉检测--相机

一&#xff0c;相机就是CCD么&#xff1f; 通常&#xff0c;我们把相机都叫作CCD&#xff0c;CCD已经成了相机的代名词。其实很可能正在使用的是CMOS。CCD以及CMOS都称为感光元件&#xff0c;都是将光学图像转换为电子信号的半导体元件。他们在检测光时都采用光电二极管&#…

5252DE 5G 外场通信测试仪

5252DE 5G 外场通信测试仪 集先进算法和高性能硬件于一体的便携式测试仪表 产品综述 5252DE 5G 外场通信测试仪是集合高性能频谱处理模块、多制式解析算法软件于一体的手持式测试仪表&#xff0c;具有很好的便携性、兼容性与可拓展性。 5252DE 具有工作频段宽、性能指标高…

ICLR24大模型提示(2/11) | BatchPrompt:多样本批量提示事半功倍

【摘要】由于大型语言模型 (LLM) 的 token 限制不断增加&#xff0c;使得长上下文成为输入&#xff0c;使用单个数据样本进行提示可能不再是一种有效的方式。提高效率的一个直接策略是在 token 限制内对数据进行批处理&#xff08;例如&#xff0c;gpt-3.5-turbo 为 8k&#xf…

深度学习Week15——利用TensorFlow实现猫狗识别2

文章目录 深度学习Week15——利用TensorFlow实现猫狗识别2—数据增强 一、前言 二、我的环境 三、前期工作 1、配置环境 2、导入数据 四、数据预处理 1、加载数据 2、可视化数据 3、检查数据 4、配置数据集 五、构建VGG-16模型 1、设置动态学习率 2、早停与保存最佳模型参数 五…