个人随笔
目录
AtomicReference<Double> 核心笔记
2026-01-09 17:34:37

一、核心概念与使用场景

1. 基本定义

AtomicReference<Double> 是 Java java.util.concurrent.atomic 包下的原子类,用于在多线程环境下,对 Double 包装类对象引用进行原子性的读取、修改和更新操作。

2. 核心作用

保证多线程环境下 Double 类型数据操作的线程安全,避免使用 synchronized 关键字带来的锁竞争开销,同时防止普通 Double 变量在多线程下出现的赋值覆盖、读取中间态等问题。

3. 适用场景

  • 多线程共享 Double 类型数据(如金额、分数、统计值等)的场景

  • 需要高效原子更新操作,无需重量级锁的场景

  • 对 Double 引用对象进行原子性替换、比较更新的场景

二、常用方法汇总

方法签名 核心作用 备注
Double get() 原子性获取当前存储的 Double 引用值 无锁操作,直接读取最新值
void set(Double newValue) 原子性设置新的 Double 值 写操作具有原子性,保证其他线程可见
boolean compareAndSet(Double expect, Double update) CAS 核心方法:若当前值等于 expect,则原子更新为 update,返回更新是否成功 乐观锁核心,失败需重试
Double getAndSet(Double newValue) 原子性设置新值,并返回更新前的旧值 “先获取再设置”,原子性完成两个操作
Double updateAndGet(UnaryOperator<Double> updateFunction) 基于当前值通过函数计算新值,原子性更新并返回新值 Java 8+ 新增,简化复杂更新逻辑
Double getAndUpdate(UnaryOperator<Double> updateFunction) 基于当前值计算新值,原子性更新并返回旧值 与 updateAndGet 互补,返回值不同

三、实战示例

1. 基础使用(单线程场景)

演示初始化、获取、设置、更新等基础操作:

  1. import java.util.concurrent.atomic.AtomicReference;
  2. public class AtomicReferenceDoubleBasicDemo {
  3. public static void main(String[] args) {
  4. // 1. 初始化:指定初始值(无参构造默认 null,需避免空指针)
  5. AtomicReference<Double> atomicDouble = new AtomicReference<>(100.0);
  6. // 2. 获取当前值
  7. Double current = atomicDouble.get();
  8. System.out.println("初始值:" + current); // 输出:初始值:100.0
  9. // 3. 直接设置新值
  10. atomicDouble.set(200.5);
  11. System.out.println("set 后的值:" + atomicDouble.get()); // 输出:set 后的值:200.5
  12. // 4. getAndSet:设置新值并返回旧值
  13. Double oldVal = atomicDouble.getAndSet(300.8);
  14. System.out.println("旧值:" + oldVal + ",新值:" + atomicDouble.get()); // 输出:旧值:200.5,新值:300.8
  15. // 5. updateAndGet:基于当前值计算(如累加 10)
  16. Double newVal = atomicDouble.updateAndGet(v -> v + 10);
  17. System.out.println("累加 10 后的值:" + newVal); // 输出:累加 10 后的值:310.8
  18. }
  19. }

2. 多线程场景(核心:CAS 原子更新)

模拟 10 个线程同时累加 Double 值,验证线程安全:

  1. import java.util.concurrent.atomic.AtomicReference;
  2. public class AtomicReferenceDoubleThreadDemo {
  3. // 共享原子 Double 变量,初始值 0.0
  4. private static final AtomicReference&lt;Double&gt; TOTAL_SCORE = new AtomicReference<>(0.0);
  5. // 累加方法:使用 CAS 保证原子性
  6. public static void addScore(Double delta) {
  7. while (true) { // CAS 失败则重试
  8. // 1. 获取当前最新值
  9. Double current = TOTAL_SCORE.get();
  10. // 2. 计算目标值
  11. Double next = current + delta;
  12. // 3. CAS 比对更新:当前值未变则更新,成功则退出
  13. if (TOTAL_SCORE.compareAndSet(current, next)) {
  14. break;
  15. }
  16. }
  17. }
  18. public static void main(String[] args) throws InterruptedException {
  19. // 创建 10 个线程,每个线程累加 10.5 分
  20. Thread[] threads = new Thread[10];
  21. for (int i = 0; i < 10; i++) {
  22. threads[i] = new Thread(() -> addScore(10.5));
  23. threads[i].start();
  24. }
  25. // 等待所有线程执行完毕
  26. for (Thread thread : threads) {
  27. thread.join();
  28. }
  29. // 预期结果:10 * 10.5 = 105.0
  30. System.out.println("最终总分:" + TOTAL_SCORE.get()); // 输出:最终总分:105.0
  31. }
  32. }

四、关键注意事项

1. 包装类与空指针问题

AtomicReference<Double> 操作的是 Double 包装类,而非基本类型 double。若使用无参构造初始化(默认值为 null),调用 get() 时可能抛出 NullPointerException,建议初始化时指定默认值(如 0.0)。

2. CAS 的 ABA 问题

CAS 操作存在 ABA 问题:当值从 A 变为 B 后又变回 A,CAS 会认为值未修改而成功更新。若业务场景需避免此问题,可使用 AtomicStampedReference<Double>(带版本号的原子引用),通过版本号判断值是否被篡改。

3. 与 AtomicDouble 的区别

特性 AtomicReference AtomicDouble
操作类型 Double 包装类(引用类型) double 基本类型
JDK 支持 JDK 1.5+ 原生支持 JDK 无原生实现,需手动封装或导入第三方包
通用性 可扩展到任意引用类型(如 AtomicReference 仅针对 double 类型,专用性强

4. 性能优势与局限

优势:基于 CAS 无锁机制,多线程竞争不激烈时,性能优于 synchronized 锁。
局限:若多线程竞争激烈,CAS 重试次数会增多,性能可能下降,此时可考虑使用 LongAdder(针对数值类型)或分段锁。

五、核心总结

  1. AtomicReference<Double> 核心价值:多线程下安全操作 Double 引用,通过 CAS 实现无锁原子更新。

  2. 核心方法优先级:简单读写用 get()/set(),条件更新用 compareAndSet(),复杂计算用 updateAndGet()

  3. 避坑要点:初始化指定默认值防 NPE,高竞争场景注意 CAS 重试问题,敏感场景用 AtomicStampedReference 解决 ABA 问题。

    (注:文档部分内容可能由 AI 生成)

 9

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2