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

synchronized的实现原理与锁升级机制

在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的内存语义与指令重排序

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) {  // 读操作
            // 执行相关操作
        }
    }
}

CAS操作与原子类实现原理

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...