AtomicLong是Java标准库java.util.concurrent.atomic包中提供的一个类,用于对long型数值进行原子性更新。它通过CAS(比较并交换)这一无锁算法保证多线程环境下的操作原子性,常被用于实现序列号生成、计数统计等场景,避免使用同步锁带来的阻塞与上下文切换开销。该类自JDK 1.5起引入,属于JSR-166并发工具包的一部分。[1]
定义
AtomicLong是Java平台java.util.concurrent.atomic包中的一个类,它封装了一个可被原子更新的long类型值。该类的全限定名为java.util.concurrent.atomic.AtomicLong,它继承自Number类并实现了Serializable接口。与普通的long变量不同,对AtomicLong的读写和修改操作在多线程环境下具有原子性,即不会被其他线程的操作打断。
AtomicLong的设计目标并非取代java.lang.Long,而是为并发场景提供单一变量的无锁访问与更新能力。它提供了get、set、getAndSet、compareAndSet以及incrementAndGet、decrementAndGet、addAndGet等一系列方法,分别用于读取、写入和原子性的加减运算。[2]按照官方文档说明,它适用于诸如原子递增的序列号一类的应用。[3]
原理
AtomicLong内部维护一个用volatile关键字修饰的long型字段value,以保证多线程下的内存可见性。类在静态初始化时会通过Unsafe获取该字段在对象中的内存偏移量,后续的原子更新便依据这个偏移量定位目标字段。其核心运算是CAS,即比较并交换:仅当内存中的当前值与预期值相等时,才把新值写入。
在JDK 8以前的实现中,自增之类的操作由一段循环构成:先读取当前值,计算出新值,再调用compareAndSet尝试更新;若失败则重新读取并重试,这种反复尝试称为自旋。[4]计算过程可概括为:$next = current + delta$,随后执行 $cas(current, next)$,失败则回到读取步骤。JDK 8起,这些方法改为直接调用Unsafe的getAndAddLong等底层方法。[5]
CAS本身依赖处理器提供的原子指令,在x86架构上可对应到单条CPU指令,因此不需要显式加锁即可完成原子更新。[6]此外,类中通过本地方法VMSupportsCS8检测底层Java虚拟机是否支持对long型数值的无锁CAS,并将结果缓存,以便在不支持时改由Java层面处理,避免使用用户可见的锁。[7]
发展历程
AtomicLong随JSR-166规范一同进入Java平台,自JDK 1.5开始提供。[8]该类的原始实现由道格·利(Doug Lea)在JCP JSR-166专家组协助下编写,并声明为公有领域。[1]在最初版本中,自增、自减等方法通过CAS自旋循环实现。[4]
JDK 1.6为AtomicLong增加了lazySet方法,提供延迟设置语义。JDK 1.8对其做了两项重要改动:一是自增等方法改为直接调用Unsafe的原子加法方法,减少CAS失败时的分支预测开销;二是新增了getAndUpdate、updateAndGet、getAndAccumulate、accumulateAndGet等方法,允许以函数式接口表达任意变换逻辑,且要求所传入的函数无副作用,因为在竞争导致更新失败时它可能被重复应用。[3]JDK 9又补充了compareAndExchange以及getPlain、getOpaque、getAcquire等用于不同内存语义访问的方法。[9]
应用
AtomicLong最典型的用途是生成原子递增的序列号。官方包文档给出的示例中,一个名为Sequencer的类以AtomicLong(17)作为内部序列号,其next方法调用getAndIncrement返回下一个值。[10]在需要为消息、订单等分配不重复编号的场合,这种方式无需额外加锁即可保证编号不重复。[5]
计数统计是另一类常见场景,例如统计多个线程各自处理的数据中满足某条件的元素个数。若没有原子类,多线程计数需要借助synchronized等同步措施,而基于CAS的非阻塞算法可以减少这类阻塞带来的性能损耗。[11]在低到中等竞争强度、且只对单一变量做简单更新的情况下,原子类通常比使用锁具有更好的吞吐表现。[12]
局限
高并发是AtomicLong最主要的限制所在。当大量线程同时更新同一个实例时,由于任一时刻只有一个线程的CAS操作能够成功,其余线程会不断自旋重试,从而持续占用CPU资源,吞吐量随之下降。[13]竞争激烈时,CAS频繁失败还会带来缓存一致性方面的额外开销。[12]
为解决这一瓶颈,JDK 1.8引入了LongAdder。其思路是把原本集中于单一变量的更新压力分散到一个Cell数组中,不同线程按各自的散列值命中不同槽位,各自只对自己槽内的值做CAS,求和时再把各槽的值汇总。[14]官方文档也说明,在多线程更新一个用于统计用途的公共总和时,LongAdder通常比AtomicLong更可取;低竞争下两者特性相近,高竞争下LongAdder的预期吞吐量明显更高,代价是占用更多空间。[15]
这一替代关系并不完整。AtomicLong提供了getAndIncrement、getAndDecrement、addAndGet、compareAndSet等较为丰富的接口,能够即时取得增减后的精确值,而LongAdder的功能相对有限,读取时也不保证强一致性。因此在需要精确控制计数或进行细粒度同步的场合,仍以AtomicLong为宜。[16]此外,CAS机制本身还面临ABA问题,即一个值从A变为B又变回A时,比较结果会误判为未发生变化,需要配合版本号一类的方案加以规避。[17]
参见
参考资料
- AtomicLong [引用日期2026-10-14]
- AtomicLong [引用日期2026-10-14]
- API reference for Java Platform, Standard Edition [引用日期2026-10-14]
- AtomicLong [引用日期2026-10-14]
- java 原子类实现原理剖析 [引用日期2026-10-14]
- How CAS related changes in AtomicLong class in Java 8 work? [引用日期2026-10-14]
- AtomicLong [引用日期2026-10-14]
- AtomicLong [引用日期2026-10-14]
- /* [引用日期2026-10-14]
- Package java.util.concurrent.atomic [引用日期2026-10-14]
- Java Review - 并发编程_原子操作类原理剖析 [引用日期2026-10-14]
- Java Atomic操作的性能如何 [引用日期2026-10-14]
- JUC包中的分而治之策略-为提高性能而生 | 并发编程网 [引用日期2026-10-14]
- Java 并发包原子操作类解析 [引用日期2026-10-14]
- 从 LongAdder 中窥见并发组件的设计思路 [引用日期2026-10-14]
- aliyun.com 上的网页 [引用日期2026-10-14]
- 1 Atomic原子操作 [引用日期2026-10-14]