一、CAS:无锁同步的"原子操作"
1. 什么是CAS?
本质:CPU硬件层面的原子指令,能保证"比较+交换"两步操作不可分割(不会被其他线程打断)。
核心逻辑:有三个参数——内存中的当前值(V)、预期值(E)、新值(N)。只有当内存值V等于预期值E时,才把V更新为N;否则不更新,返回旧的V。
类比:你想给冰箱里的牛奶换个新牌子(N),先确认现在的牛奶是不是你以为的牌子(E),如果是就换掉,不是就不换,整个过程没人能中途"插队"。
2. Java中怎么用CAS?
通过Unsafe类实现(底层是native方法,调用CPU指令),比如compareAndSwapInt方法,需要传入对象实例、字段的内存偏移量、预期值、新值。
例:用Unsafe修改对象的某个字段,只有字段当前值和预期值一致时才成功。
3. CAS的优缺点
优点:无锁(线程不阻塞),比synchronized等悲观锁效率高。
缺点:
自旋开销大:如果CAS一直失败(比如高并发冲突),线程会循环重试,浪费CPU资源。
只能原子操作一个变量:无法直接处理多个变量的原子性。
ABA问题:线程1准备把A改成C,但线程2先把A改成B又改回A,线程1误以为A没变化,导致错误更新。
4. 解决ABA问题
用版本号标记值的变化:每次修改值时,版本号+1。Java提供AtomicStampedReference(带版本号的原子引用),比较时不仅比较值,还要比较版本号。
简化版:AtomicMarkableReference(只记录值是否被修改过,用boolean标记)。
二、Java原子操作类:线程安全的"变量工具包"
Java的java.util.concurrent.atomic包提供了多种原子操作类,避免了手动写CAS的复杂逻辑,直接保证变量操作的线程安全。
1. 基本类型原子类(如AtomicInteger)
功能:原子更新int、long、boolean等基本类型,比如自增(incrementAndGet)、累加(addAndGet)。
原理:底层用CAS自旋实现,失败就重试,直到成功。
例:多线程同时调用incrementAndGet,最终结果一定是正确的(不会少加)。
2. 引用类型原子类(如AtomicReference)
功能:原子更新对象引用,保证修改对象引用时的线程安全。
例:用AtomicReference原子地把对象A换成对象B,避免多线程同时修改引用导致的混乱。
3. 数组类型原子类(如AtomicIntegerArray)
功能:原子更新数组中的元素,比如修改数组指定索引的值、累加元素。
例:多线程同时修改数组的不同位置,每个位置的操作都是原子的。
4. 对象属性原子修改器(如AtomicIntegerFieldUpdater)
功能:原子更新对象的某个volatile字段(非静态、非final),无需把字段定义为原子类。
约束:字段必须是volatile(保证可见性),且调用者能访问该字段(如public或同包)。
5. 高性能累加器(LongAdder/DoubleAdder)
解决问题:高并发下AtomicLong的CAS自旋冲突严重,性能下降。
原理:把一个值分散到多个"槽"(Cell数组)中,不同线程操作不同槽,减少冲突;需要结果时,累加所有槽和一个基础值(base)。
特点:高并发写场景性能远超AtomicLong,但sum()方法返回的是近似值(因为累加时可能有线程还在修改槽)。
总结
CAS是并发安全的底层基石,通过硬件原子指令实现无锁同步,但有自旋开销、ABA等问题。
原子操作类是CAS的"封装工具",覆盖基本类型、引用、数组、对象属性等场景,简化并发编程。
LongAdder是高并发下的性能优化方案,通过分散热点提升写效率,适合"写多 read少"的场景。
这些技术共同构成了Java并发编程中轻量级、高效的线程安全解决方案。