doubleadder比synchronized+double更合适,因其采用分段累加、无锁设计,避免全局锁瓶颈和装箱开销,支持高并发且精度更可控;正确用法是直接new doubleadder()后调用add(double),禁用读-改-写模式。

Java 中用 Double 包装类做累加(比如反复执行 sum = sum + value)在高并发下既线程不安全,又因浮点数精度和对象创建开销导致结果不可靠。直接用 DoubleAdder 是更优解——它专为高并发浮点累加设计,无锁、低竞争、精度可控。
DoubleAdder 为什么比 synchronized + Double 更合适
DoubleAdder 内部采用分段累加(类似 LongAdder),每个线程优先更新自己的 cell,冲突时才扩容或重试,避免了全局锁的串行瓶颈。而 synchronized 块包裹 Double 对象操作,不仅锁粒度大,还每次都要拆箱/装箱、创建新对象,既慢又易引发 GC 压力和精度漂移(因 Double.valueOf() 缓存有限,非缓存值每次都新建对象)。
- 不依赖对象引用变更,避免
Double不可变性带来的频繁新建 - 底层用
volatile double[]和 CAS 操作,无锁但保证最终一致性 - 最终调用
sum()时才合并所有 cell,开销集中且可控
正确初始化和累加方式
声明时直接用无参构造:DoubleAdder adder = new DoubleAdder();。累加统一用 add(double x),不要手动读-改-写(如 adder.sum() + x),否则失去并发安全性和性能优势。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- ✅ 正确:
adder.add(3.14); adder.add(2.718); - ❌ 错误:
double s = adder.sum(); adder = new DoubleAdder(); adder.add(s + x);(破坏原子性,且新建实例丢弃历史) - ⚠️ 注意:
add()接受double基本类型,不是Double对象,避免隐式装箱
精度控制与 sum() 的使用时机
DoubleAdder 本身不提升浮点精度,它只是更可靠地聚合多个 double 值。精度问题本质来自 IEEE 754 表示限制,无法根除,但能减少误差累积路径:
- 避免在循环中频繁调用
sum()(每次遍历所有 cell,且可能因并发导致中间态偏差) - 适合“只写多读”场景:大量线程并发 add,最后单次调用
sum()获取最终结果 - 若需更高精度,可在业务层将关键计算转为
BigDecimal,但注意BigDecimal非线程安全且性能远低于DoubleAdder,不可直接替代
和 AtomicDouble 的关键区别
AtomicDouble(JDK 17+ 引入)提供原子读写和 CAS 更新,但仍是单变量模型,高并发下竞争激烈;DoubleAdder 在争抢严重时自动扩容 cell 数组,吞吐量随线程数增长更平缓。
- 低并发(≤ 4 线程):两者差异不大,
AtomicDouble语义更直观 - 高并发(数十线程以上):
DoubleAdder吞吐通常高 3–10 倍,尤其在累加密集场景 -
DoubleAdder不支持 compareAndSet 或 getAndAdd 等细粒度原子操作,只专注“累加”这一件事
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










