LongAdder和AtomicLong的区别?

1 个回答

  • 刘峰
    杯中之物从来不能消愁补过
    #### 1. **基本概念** - **`AtomicLong`**: - 基于 **CAS(Compare-And-Swap)** 实现,是 JDK 提供的一种用于保证线程安全的原子性递增操作。 - 在多线程环境下,如果存在高并发竞争,`CAS` 操作可能会导致大量重试,从而影响性能。 - **`LongAdder`**: - JDK 1.8 引入,为了解决 `AtomicLong` 在高并发情况下性能不佳的问题。 - 通过分段技术,利用多个计数单元(`Cell`)分散并发竞争,提升性能。 #### 2. **实现原理** ##### **`AtomicLong` 实现原理** - 基于 `CAS`(硬件支持的原子性指令)。 - 底层通过 `Unsafe` 类的 `compareAndSet` 方法直接操作内存,实现对 `volatile` 变量的原子更新。 常见方法: ```java public final boolean compareAndSet(long expect, long update) { return unsafe.compareAndSwapLong(this, valueOffset, expect, update); } ``` - 竞争激烈时可能会出现频繁的 `CAS` 重试,从而降低性能。 ##### **`LongAdder` 实现原理** - 基于 **分段累加** 的思想,依赖于 `Striped64` 抽象类实现。 - `LongAdder` 通过一个 `base` 变量和一个 `cells` 数组来存储计数值: - `base`:低竞争时,直接通过 `CAS` 更新。 - `cells`:高竞争时,根据当前线程的哈希值将其分散到 `cells` 数组的不同位置,每个 `Cell` 独立存储一个计数值。 工作流程: 1. 先尝试通过 `CAS` 更新 `base`。 2. 如果 `CAS` 失败,说明竞争激烈,当前线程会尝试分配或使用一个 `Cell`。 3. 最终结果通过遍历 `base` 和所有 `Cell` 的值累加得出。 代码示例(`sum` 方法): ```java public long sum() { Cell[] as = cells; long sum = base; if (as != null) { for (Cell a : as) { if (a != null) { sum += a.value; } } } return sum; } ``` #### 3. **优缺点** | 特性 | `AtomicLong` | `LongAdder` | |------------------|--------------------------------------|-----------------------------------------| | 实现机制 | 单点更新,基于 `CAS` | 分段累加,基于 `CAS` 和 `Cell` 分散竞争 | | 性能 | 并发竞争高时性能较差(频繁重试) | 并发竞争高时性能更优 | | 内存开销 | 占用空间小 | 空间换时间,占用更多内存 | | 数据准确性 | 精确 | 并发统计中可能会有短暂不精确 | #### 4. **适用场景** - **`AtomicLong`**: - 适合低并发场景。 - 对数据精确度要求较高的业务,例如银行账户余额更新。 - **`LongAdder`**: - 适合高并发场景。 - 对数据精确度要求相对较低的业务,例如网站访问量、点赞数、统计计数等。 #### 5. **`LongAdder` 的结果可能不准确** 在并发更新时,`LongAdder` 的累加结果可能并非一个精确的“快照”,因为: - `sum` 方法在计算总和时,可能有其他线程正在对 `base` 或 `cells` 进行更新。 - JDK 源码明确说明: > Returns the current sum. The returned value is NOT an atomic snapshot; invocation in the absence of concurrent updates returns an accurate result, but concurrent updates that occur while the sum is being calculated might not be incorporated. 尽管如此,对于一些统计类场景,这种“不精确性”通常是可以接受的。 #### 6. **扩展:`DoubleAdder`** - `DoubleAdder` 是 JDK 1.8 提供的另一个分段累加类,专门用于浮点数的原子操作。 - 与 `LongAdder` 实现原理相同,只是存储的数据类型变成了 `double`。 ### 总结 - `AtomicLong` 适合低并发、精确度要求高的场景。 - `LongAdder` 适合高并发、对数据精确度要求不高的场景。 - `LongAdder` 通过分散竞争有效提升性能,但在计算过程中可能会有一定的不精确性,具体选用需视业务需求而定。
    发布于 2025-01-23 05:39 •IP属地 江西
    举报
    评论
    收藏