详细分析LongAdder原理

前言

适合阅读者:看了源码但没看懂,看得头大的
本文参考源码:JDK8版本
本文作用:
1.深入理解LongAdder原理
2.辅助源码阅读

阅读建议:配合JDK8源码阅读

总体过程就是这样

截图

详细分析

1.类的结构是怎样的

继承了Striped64抽象类,这个抽象类中有cells数组,base变量,有longAccumulate方法

LongAdder里自己定义了add方法和sum方法.


2.调用add(x)方法时,发生了什么

第一步.先判断cells有没有被初始化

1.没有过初始化------代表此时没有竞争发生过,CAS累加base变量

1.1 累加成功-----add方法执行完毕

1.2 累加失败----此时进入第二步

2.cells有过初始化-----代表此时发生过竞争,进入第二步

第二步.当1.cells数组有过初始化, 2.当前线程需要操作的cells数组中的那个位置已经创建了一个cells对象 3.CAS累加数据成功,则弹出add方法


这三个条件有任意一个未满足,调用longAccumulate方法


3.调用longAccumulate方法,发生了什么

调用这个方法的原因—解决上述三个情况任意一种

根据进入longAccumulate方法的三种情况,可以知道longAccumulate就是为了解决这三个情况下如何实现累加操作而诞生的

1.处理 Cells 数组未初始化的情况

  1. 检查cellsBusy(锁标记),如果为0(表示无锁),尝试加锁(置1),加锁成功进入下一步
  2. 初始化长度为2的cells数组
  3. 根据add(x)时传入的x,创建一个值为x的cell对象,根据当前线程的hash值&1得出存放到cells数组的位置,放入
  4. 释放cellBusy(置0).
  5. 结束方法

如果获取cellsBusy失败的话-----失败回退:如果其他线程正在初始化(cellsBusy 被占用),则尝试直接通过 CAS 累加到 base 变量(兜底方案)。


2.处理当前线程在cells数组中需要操作的位置未被占用(该位置为 null)的情况

  1. cellsBusy是否为0,为0进入下一步
  2. 创建新 Cell:生成一个值为 x 的新 Cell 对象。
  3. 再次检查 cellsBusy 锁,确保无竞争后,CAS 加锁。
  4. 二次检查:加锁后再次确认该位置仍为 null(避免其他线程已修改)。
  5. 插入 Cell:将新 Cell 放入数组对应位置,释放锁(置0)。
  6. 结束方法.

如果获取cellBusy锁失败-----失败重试:如果插入过程中被其他线程抢占(如该位置已被填充),则回到循环重试。

3.处理 当前线程在cells数组中需要操作的位置存在竞争(CAS 操作失败)的情况

1.把wasUncontended设置为true,为的是调用一次advanceProbe(h)重新生成当前线程hash值,换一下当前线程需要操作的位置

2.尝试两次CAS**(每一次都重新生成当前线程hash值,使用新的所要累加的位置,)**,累加当前位置上的值(当前值加x),

2.1 如果有一次CAS成功,结束方法(退出这个情况的唯一可能)

2.2 如果两次都失败,进入判断是否进入需要扩容,需要就扩容.不需要就不断重试

3.数组是否需要扩容

3.1 当数组长度小于CPU核数,扩容:通过 CAS 获取 cellsBusy 锁(置1),将数组大小翻倍,复制旧数据到新数组,释放锁(置0)。重新执行操作2.

3.2 当数组长度大于等于CPU核数,不再扩容,调用advanceProbe(h)重新生成当前线程需要累加的位置,不断重试操作2,直到成功


我写的这个步骤其实有错误,这些步骤里的返回到操作2是错误的,实际上返回到的是第二个情况(判断当前线程在cells数组中需要操作的位置未被占用(该位置为 null))那里,这里为了理解起来方便些,描述为返回到操作2

<think>我们正在讨论Java的AtomicInteger实现原理。根据引用[2]和[3],AtomicInteger底层使用了CAS(Compare and Swap)操作,并且依赖于Unsafe类来直接操作内存。同时,引用[3]展示了AtomicInteger的内部结构,包括一个volatile修饰的int值value,以及通过Unsafe获取的value字段在对象中的偏移地址valueOffset。 实现原理: 1. AtomicInteger内部使用volatile int value来存储实际的值,保证可见性。 2. 通过Unsafe类的CAS方法(compareAndSwapInt)来实现原子更新。CAS操作需要三个参数:对象实例、字段偏移量、预期值和新值。 3. 当多个线程尝试更新同一个AtomicInteger时,CAS操作会确保只有一个线程能成功更新,其他线程需要重试。 以incrementAndGet()方法为例,其内部通常是一个循环,不断尝试将当前值加1,直到CAS操作成功为止。 另外,引用[5]提到了ABA问题:即一个值从A变成B又变回A,CAS操作会误认为没有变化。解决方法是使用版本号(如AtomicStampedReference)。 现在,根据用户的问题,我们需要详细解释AtomicInteger的实现原理和底层机制。</think>### Java AtomicInteger 实现原理及底层机制 #### 一、核心实现原理:CAS 操作 AtomicInteger 的原子性操作基于 **CAS (Compare-And-Swap)** 机制: 1. **CAS 操作流程**: ```java while(true) { int current = get(); // 读取当前值 int next = current + delta; // 计算新值 if(compareAndSet(current, next)) // CAS 原子更新 break; // 成功则退出 } ``` 2. **底层硬件支持**: - 通过 CPU 指令(如 x86 的 `CMPXCHG`)实现原子操作 - 无需加锁即可保证线程安全[^2][^3] #### 二、关键组件分析 ```java public class AtomicInteger extends Number { // 1. 通过Unsafe突破JVM限制 private static final Unsafe unsafe = Unsafe.getUnsafe(); // 2. 实际存储的值(volatile保证可见性) private volatile int value; // 3. 字段内存偏移量(关键定位信息) private static final long valueOffset; static { try { // 计算value字段在对象内存布局中的精确位置 valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { /* 处理异常 */ } } } ``` [^3] #### 三、核心操作示例 1. **incrementAndGet() 源码**: ```java public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; } ``` - `getAndAddInt` 内部使用自旋CAS实现[^3] 2. **compareAndSet() 实现**: ```java public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } ``` - 直接调用Unsafe的CAS指令[^3] #### 四、解决ABA问题 当存在 **A→B→A** 的值变化时,普通CAS可能误判: ```java // 普通CAS可能误接受ABA场景 atomicInt.compareAndSet(A, C); // 可能成功(实际存在中间状态) ``` 解决方案: 1. **AtomicStampedReference**: ```java AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(A, 0); ref.compareAndSet(A, C, stamp, stamp+1); // 需同时验证版本号 ``` 2. **版本号机制**: - 每次更新伴随版本号递增 - 同时检查值和版本号[^5] #### 五、性能优势 | 实现方式 | 性能特点 | 适用场景 | |----------------|------------------------------|------------------| | **synchronized** | 线程阻塞,上下文切换开销大 | 复杂同步逻辑 | | **AtomicInteger**| 无锁自旋,CPU密集型操作 | 简单计数器等高频场景 | > **注**:高竞争场景下自旋可能导致CPU资源浪费,此时需考虑LongAdder等分段计数方案。 #### 六、典型应用场景 1. 并发计数器(如网站访问量统计) 2. 状态标志位(如服务启动/停止状态) 3. 序列号生成器 4. 无锁队列实现基础组件 > **最佳实践**:优先使用`java.util.concurrent.atomic`中的原子类,避免直接操作Unsafe[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值