Java并发编程核心技术:synchronized、volatile与CAS

在Java虚拟机层面,synchronized关键字通过对象头的Mark Word实现锁状态管理。每个Java对象在堆内存中都包含对象头,其中存储了锁状态、GC分代年龄等信息。

对象头的Mark Word在不同锁状态下会发生变化:

  • 无锁状态:存储对象的hashCode和分代年龄
  • 偏向锁:存储持有偏向锁的线程ID
  • 轻量级锁:指向栈中锁记录的指针
  • 重量级锁:指向互斥量(mutex)的指针

锁升级过程遵循以下路径:

  1. 偏向锁:当第一个线程访问同步块时,JVM启用偏向锁模式
  2. 轻量级锁:当第二个线程尝试获取锁时,升级为轻量级锁(自旋锁)
  3. 重量级锁:当自旋超过阈值(默认10次)或等待线程超过CPU核心数的一半时升级

示例代码展示不同使用场景:

// 实例方法同步
public synchronized void instanceMethod() {
// 临界区代码
}
// 静态方法同步
public static synchronized void staticMethod() {
// 临界区代码
}
// 同步代码块
public void codeBlock() {
Object lock = new Object();
synchronized(lock) {
// 临界区代码
}
}

volatile关键字通过内存屏障(Memory Barrier)实现两大特性:

  1. 可见性保证:写操作会立即刷新到主内存
  2. 禁止指令重排序:编译器优化和处理器指令重排受限

JVM层面的内存屏障策略:

  • LoadLoad屏障:确保本读操作先于后续读操作
  • StoreStore屏障:确保本写操作先于后续写操作
  • LoadStore屏障:确保读操作先于后续写操作
  • StoreLoad屏障:确保写操作先于后续读操作

典型应用场景分析:

public class VolatileExample {
private volatile boolean flag = false;
public void writer() {
flag = true; // 写操作
}
public void reader() {
if (flag) { // 读操作
// 执行相关操作
}
}
}

Compare And Swap(CAS)通过CPU原子指令实现无锁编程,核心公式: CAS(V, E, N) = 当且仅当V的值等于E时,将V的值设为N

Java中的Unsafe类提供CAS底层支持:

public final class Unsafe {
public final native boolean compareAndSwapInt(
Object o, long offset, int expected, int x);
// 其他CAS方法
}

AtomicInteger源码解析:

public class AtomicInteger extends Number {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
}

性能对比测试数据(示例): | 操作类型 | 10线程/ms | 100线程/ms | 1000线程/ms | |---------------|---------|----------|-----------| | synchronized | 32 | 285 | 4231 | | volatile读 | 12 | 98 | 1052 | | CAS操作 | 8 | 45 | 387 |

选型决策树:

  1. 需要互斥访问 → 选择synchronized
  2. 仅需可见性保证 → 使用volatile
  3. 高并发计数器场景 → 优先考虑Atomic类
  4. 复杂原子操作 → 使用LongAdder或Accumulator

synchronized典型问题:

  1. 死锁案例:
// 线程1
synchronized(resourceA) {
synchronized(resourceB) { ... }
}
// 线程2
synchronized(resourceB) {
synchronized(resourceA) { ... }
}

解决方案:使用ReentrantLock的tryLock()方法

volatile使用误区:

// 错误用法:复合操作不具备原子性
private volatile int count = 0;
public void unsafeIncrement() {
count++; // 实际包含读-改-写三个操作
}

CAS的ABA问题解决方案:

AtomicStampedReference<Integer> atomicRef =
new AtomicStampedReference<>(0, 0);
// 更新时同时检查值和版本戳
atomicRef.compareAndSet(0, 1, 0, 1);

synchronized的monitor实现:

// HotSpot虚拟机源码片段
ObjectMonitor() {
_header = NULL;
_count = 0; // 重入次数
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL; // 持有线程
_WaitSet = NULL; // 等待队列
_EntryList = NULL; // 竞争队列
}

内存屏障的具体实现(x86架构):

  • StoreStore屏障:对应"lock; addl $0x0,(%rsp)"
  • LoadLoad屏障:通过mfence指令实现
  • StoreLoad屏障:使用mfence或xchg指令

CPU缓存一致性协议(MESI):

  • Modified(修改):缓存行已被修改
  • Exclusive(独占):缓存行未被修改
  • Shared(共享):缓存行只读
  • Invalid(无效):缓存行数据过期
正文到此结束
评论插件初始化中...
Loading...
本文目录