🌟 大家好,我是摘星! 🌟
今天为大家带来的是并发设计模式实战系列,第十四章CAS(无锁编程),废话不多说直接开始~
目录
一、核心原理深度拆解
1. CAS原子操作原理
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 内存值 (V) │───> │ 预期值 (A) │───> │ 新值 (B) │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
└───── 比较 V == A ─────┘ │
│ │
▼ ▼
true: 更新V=B false: 操作失败
- 硬件支持:通过CPU的
CMPXCHG
指令实现(x86架构) - Java实现:
sun.misc.Unsafe
类或AtomicXXX
系列封装
2. 无锁编程优势
- 无阻塞:线程失败后重试而非挂起
- 高吞吐:减少线程上下文切换
- 无死锁:不存在资源竞争导致的死锁
二、生活化类比:超市储物柜系统
系统组件 | 现实类比 | CAS对应行为 |
储物柜状态 | 柜门指示灯 | 内存值V(红=占用/绿=空闲) |
用户操作 | 投币使用 | 预期值A(必须看到绿灯) |
系统响应 | 分配柜子并变红灯 | 新值B(修改为红灯状态) |
冲突处理 | 其他人抢先投币 | CAS返回false,用户重试 |
- 关键特征:用户无需排队(无锁),通过不断重试保证最终成功
三、Java代码实现(生产级Demo)
1. 基于AtomicInteger的计数器
import java.util.concurrent.atomic.AtomicInteger;
public class CASCounter {
private final AtomicInteger value = new AtomicInteger(0);
// 无锁递增
public int increment() {
int oldValue, newValue;
do {
oldValue = value.get(); // 读取当前值(A)
newValue = oldValue + 1; // 计算新值(B)
} while (!value.compareAndSet(oldValue, newValue)); // CAS操作
return newValue;
}
public static void main(String[] args) {
CASCounter counter = new CASCounter();
// 模拟10个线程并发计数
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter.increment();
}
}).start();
}
// 等待所有线程完成
try { Thread.sleep(2000); } catch (InterruptedException e) {}
System.out.println("Final value: " + counter.value.get()); // 正确输出10000
}
}
2. 手动实现简单CAS
import sun.misc.Unsafe;
import java.lang.reflect.Field;
class ManualCAS {
private volatile int value;
private static final Unsafe unsafe;
private static final long valueOffset;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
valueOffset = unsafe.objectFieldOffset(ManualCAS.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
public boolean compareAndSwap(int expected, int newValue) {
return unsafe.compareAndSwapInt(this, valueOffset, expected, newValue);
}
}
四、横向对比表格
1. 同步方案对比
方案 | 原理 | 吞吐量 | 适用场景 |
synchronized | 互斥锁 | 低 | 简单同步需求 |
ReentrantLock | 可重入锁 | 中 | 需要高级功能(如公平性) |
CAS | 乐观锁 | 高 | 多读少写场景 |
volatile | 可见性保证 | 最高 | 单一变量状态标志 |
2. Java原子类对比
类名 | 底层实现 | 特性 |
| CAS + volatile | 整型原子操作 |
| CAS + volatile | 对象引用原子操作 |
| 分段CAS | 超高并发计数场景 |
| CAS + 版本号 | 解决ABA问题 |
五、高级优化技巧
1. 减少CAS冲突
// 使用ThreadLocal分散热点
ThreadLocalRandom.current().nextInt(10); // 随机退避时间
// 伪代码示例
while (!casUpdate()) {
int backoff = ThreadLocalRandom.current().nextInt(10);
LockSupport.parkNanos(backoff); // 随机休眠
}
2. 解决ABA问题
AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);
// 更新时检查版本号
int stamp = ref.getStamp();
String current = ref.getReference();
ref.compareAndSet(current, "B", stamp, stamp + 1);
3. 性能监控指标
// CAS成功率统计
AtomicLong successCount = new AtomicLong();
AtomicLong totalAttempts = new AtomicLong();
public int incrementWithStats() {
int oldValue, newValue;
do {
totalAttempts.incrementAndGet();
oldValue = value.get();
newValue = oldValue + 1;
} while (!value.compareAndSet(oldValue, newValue));
successCount.incrementAndGet();
return newValue;
}
六、典型应用场景
1. 高性能计数器
// 使用LongAdder替代AtomicLong(JDK8+)
LongAdder hitCounter = new LongAdder();
void recordHit() {
hitCounter.increment(); // 内部使用分段CAS优化
}
2. 无锁队列实现
// 基于AtomicReference的MPSC队列(多生产者单消费者)
class LockFreeQueue<E> {
private static class Node<E> {
E item;
AtomicReference<Node<E>> next;
Node(E item) { this.item = item; }
}
private final AtomicReference<Node<E>> head;
private final AtomicReference<Node<E>> tail;
public void enqueue(E item) {
Node<E> newNode = new Node<>(item);
Node<E> oldTail;
do {
oldTail = tail.get();
} while (!tail.compareAndSet(oldTail, newNode));
oldTail.next.set(newNode);
}
}
3. 状态机切换控制
// 使用AtomicInteger实现线程安全状态机
enum State { INIT, RUNNING, STOPPED }
AtomicInteger currentState = new AtomicInteger(State.INIT.ordinal());
boolean start() {
return currentState.compareAndSet(
State.INIT.ordinal(),
State.RUNNING.ordinal()
);
}
七、陷阱与规避方案
1. ABA问题解决方案对比
方案 | 实现方式 | 开销 | 适用场景 |
版本号戳 | AtomicStampedReference | 中 | 需要严格版本控制 |
布尔标记 | AtomicMarkableReference | 低 | 简单状态标记 |
延迟回收 | 对象池+生命周期管理 | 高 | 复杂对象场景 |
2. 活锁风险示例与解决
// 错误示例:线程间互相导致CAS失败
void transfer(AtomicInteger acc1, AtomicInteger acc2, int amount) {
while (true) {
int old1 = acc1.get();
if (!acc1.compareAndSet(old1, old1 - amount)) continue;
int old2 = acc2.get();
if (!acc2.compareAndSet(old2, old2 + amount)) {
acc1.compareAndSet(old1 - amount, old1); // 回滚导致活锁
continue;
}
break;
}
}
// 解决方案:引入随机退避
if (!acc2.compareAndSet(old2, old2 + amount)) {
acc1.compareAndSet(old1 - amount, old1);
Thread.sleep(ThreadLocalRandom.current().nextInt(10)); // 关键点
continue;
}
3. 性能陷阱排查表
现象 | 可能原因 | 排查工具 |
CPU占用100% | CAS自旋过多 | JStack采样热点方法 |
操作成功率低于50% | 竞争激烈 | JMX监控CAS成功率 |
延迟波动大 | 缓存行伪共享 | JOL工具分析对象布局 |
4. 伪共享解决方案
// 使用@Contended注解(JDK8+)
import jdk.internal.vm.annotation.Contended;
class CounterCell {
@Contended // 避免伪共享
volatile long value;
}
// 或手动填充(兼容旧版本)
class ManualPadding {
volatile long value;
long p1, p2, p3, p4, p5, p6; // 填充至64字节(典型缓存行大小)
}
八、终极对比:CAS vs 锁(新增)
1. 微观性能对比(纳秒级操作)
指标 | CAS(AtomicInteger) | 悲观锁(synchronized) |
单线程耗时 | 12ns | 25ns |
4线程竞争耗时 | 180ns | 600ns |
16线程竞争耗时 | 2200ns | 5000ns+ |
上下文切换次数 | 0 | 线程数×操作次数 |
测试环境:MacBook Pro M1, JDK17
2. 选型决策树
是否共享变量修改?
├─否 → volatile足够
└─是 → 是否满足以下全部条件?
├─是 → 使用CAS
│ ├─线程竞争适中(<20线程)
│ ├─操作足够简单(单变量修改)
│ └─能容忍偶尔失败
└─否 → 选择锁
├─需要条件等待 → ReentrantLock
└─简单同步 → synchronized
九、JVM层优化
1. 内存屏障与CAS的关系
// 手动插入内存屏障示例(Unsafe类)
public class MemoryBarrierDemo {
private volatile int flag;
private int data;
void publish() {
data = 42; // 普通写
Unsafe.getUnsafe().storeFence(); // 写屏障
flag = 1; // volatile写
}
void consume() {
while (flag != 1) { /* 自旋 */ } // volatile读
Unsafe.getUnsafe().loadFence(); // 读屏障
System.out.println(data); // 保证看到42
}
}
- JVM内存屏障类型:
-
LoadLoad
:禁止屏障前后的读操作重排序StoreStore
:禁止屏障前后的写操作重排序LoadStore
:禁止读之后写重排序StoreLoad
:全能屏障(最昂贵)
2. 逃逸分析与栈上分配
// 通过局部变量避免CAS竞争
public class LocalCounter {
public long sum() {
final long[] localSum = new long[1]; // 栈上分配(逃逸分析优化)
IntStream.range(0, 100).parallel().forEach(i -> {
localSum[0] += i; // 线程本地计算
});
return localSum[0]; // 最终合并
}
}
- 优化效果:减少对AtomicLong等CAS对象的竞争
3. JIT编译优化策略
// 热点代码的循环展开(JIT自动优化)
@Benchmark
public void testCAS(Blackhole bh) {
AtomicInteger counter = new AtomicInteger();
for (int i = 0; i < 1000; i++) {
counter.incrementAndGet(); // 可能被JIT展开为批量CAS
}
bh.consume(counter);
}
- JVM参数:
-XX:+UnlockDiagnosticVMOptions
-XX:+PrintAssembly
-XX:+LogCompilation
十、硬件级调优
1. CPU缓存行优化
// 伪共享解决方案对比
@Contended // JDK8+官方方案(需开启-XX:-RestrictContended)
class ContendedCounter {
volatile long value1;
volatile long value2;
}
// 手动填充方案(兼容旧JDK)
class ManualPaddingCounter {
volatile long value1;
long p1, p2, p3, p4, p5, p6, p7; // 填充56字节
volatile long value2;
}
- 缓存行大小检测(Linux系统):
cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
2. NUMA架构优化
// 绑定线程到指定CPU核(Linux)
public class NumaAffinity {
static {
System.loadLibrary("numa_affinity");
}
public native void bindToCore(int coreId);
public static void main(String[] args) {
new NumaAffinity().bindToCore(2); // 绑定到2号核
// 执行敏感计算...
}
}
- JVM参数:
-XX:+UseNUMA
-XX:+UseParallelGC
3. 指令级并行优化
// 利用CAS批量操作减少总线锁定
class BatchUpdater {
private final AtomicLongArray values;
void batchUpdate(int[] indices, long[] updates) {
for (int i = 0; i < indices.length; i++) {
int idx = indices[i];
long expect, update;
do {
expect = values.get(idx);
update = expect + updates[i];
} while (!values.compareAndSet(idx, expect, update));
}
}
}
- CPU指令优化:
-
LOCK CMPXCHG16B
:x86的16字节CAS指令PAUSE
:减少自旋时的能耗(Spin Loop Hint)
十一、终极性能对比
1. 不同层级优化的效果对比
优化层级 | 吞吐量提升 | 延迟降低 | 适用场景 |
算法优化 | 10x | 50% | 数据结构选择 |
JVM调优 | 2x | 30% | 内存敏感型任务 |
硬件感知 | 1.5x | 20% | CPU密集型计算 |
指令级优化 | 1.2x | 10% | 超高频CAS操作 |
2. 全链路优化示例
// 终极优化版计数器(结合所有优化技术)
@Contended
public class UltimateCounter {
@Contended
private volatile long cell1;
@Contended
private volatile long cell2;
private static final sun.misc.Unsafe UNSAFE;
private static final long cell1Offset;
static {
try {
Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
UNSAFE = (sun.misc.Unsafe) f.get(null);
cell1Offset = UNSAFE.objectFieldOffset(
UltimateCounter.class.getDeclaredField("cell1"));
} catch (Exception e) { throw new Error(e); }
}
public void increment() {
long expect;
do {
expect = UNSAFE.getLongVolatile(this, cell1Offset);
} while (!UNSAFE.compareAndSwapLong(
this, cell1Offset, expect, expect + 1));
}
}
- 优化要点:
-
- 使用
@Contended
避免伪共享 - 直接调用
Unsafe
绕过AtomicLong
封装 getLongVolatile
保证可见性- 精确控制内存语义
- 使用