资讯中心

Java并发编程之CAS

📅 2026/11/4 12:16:24
Java并发编程之CAS
一、 概念什么是 CASCAS比较并交换是一种无锁并发的原子操作策略属于乐观锁。它包含三个操作数V (Value)内存中的实际当前值。A (Expected)预期原值旧值。B (New)准备更新的新值。核心逻辑当且仅当内存值V等于预期值A时才将内存值修改为B。否则说明值已经被其他线程改过了本次操作失败。整个过程是一个不可分割的原子操作。二、 底层实现从 Java 到 CPU 指令CAS 在 Java 中的实现是一条自顶向下的调用链Java 应用层我们通常使用AtomicInteger等原子类或者ReentrantLock的底层。Java API 层调用sun.misc.Unsafe类提供的native方法如compareAndSwapInt。JNI 桥接层unsafe.cpp通过对象引用和内存偏移量Offset计算出属性的绝对物理地址。将值交给底层原子操作方法Atomic::cmpxchg。硬件指令层在 x86 架构下最终编译为带有lock前缀的cmpxchg指令。原子性lock前缀会锁定缓存行或总线保证“读-比较-写”的硬件级别原子性。可见性/有序性lock前缀自带全内存屏障Memory Barrier效果强制刷新主存并禁止指令重排序。三、 优点与缺点优点无锁并发性能极高无需加锁不涉及操作系统的互斥量Mutex避免了用户态与内核态的上下文切换开销。非阻塞如果 CAS 失败线程不会像synchronized那样被挂起阻塞而是可以继续尝试自旋或者去做别的事情。缺点只能保证一个变量的原子操作对于多个变量同时修改CAS 无能为力可以使用AtomicReference封装为一个对象或者干脆加锁。ABA 问题详见下文。四、 存在的问题及解决方案核心考点CAS 虽然好但它天生存在三大“隐患”1. 自旋开销大 (CPU 开销)问题如果 CAS 一直失败高并发场景Java 层的do-while循环自旋会一直占用 CPU 时间片空转。如果长时间不成功会给 CPU 带来极大的开销。解决控制自旋次数如 JVM 的自适应自旋。或者直接退化为锁如synchronized的锁膨胀。或者使用LongAdder分散热点数据类似分段锁的思想。2. ABA 问题最经典问题假设线程 1 执行 CAS 时期望内存值为A。但在此时线程 2 抢先将内存改成了B然后又改回了A。此时线程 1 的 CAS 操作依然会成功但线程 1 并不知道这中间发生过变化比如链表节点的值被删了又加回来可能导致引用错乱。解决引入版本号Version。每次修改不仅修改值还要修改版本号类似数据库的乐观锁。Java 提供了AtomicStampedReference类内部用int stamp记录版本或者使用AtomicMarkableReference用 boolean 记录是否被修改过。3. 只能保证一个共享变量的原子操作问题一个 CAS 指令只能针对一个内存地址一个变量操作。解决将多个变量封装成一个对象使用AtomicReference对对象进行整体 CAS 替换。使用synchronized或Lock直接加锁。 总结一句话Java 的 CAS 是通过Unsafe类的本地方法最终依靠 CPU 的lock cmpxchg指令实现硬件级别的原子性和可见性。它以极低的性能开销取代了传统的悲观锁但必须防范其高并发下的 CPU 自旋开销以及常见的 ABA 问题。

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案