Java并发编程核心机制:synchronized、volatile与CAS原理剖析

从字节码层面看synchronized的实现,会发现它通过monitorentermonitorenter指令实现同步控制。当线程执行到同步块时,会尝试获取对象的监视器锁(monitor),这个锁信息存储在对象头的Mark Word中。JDK6之后引入的偏向锁、轻量级锁优化,使得无竞争情况下的同步开销大幅降低。以32位JVM为例,对象头的Mark Word在不同锁状态下会有不同的结构:

// 对象头结构示例(32位)
// 无锁状态:25位哈希码|4位分代年龄|1位偏向模式|2位锁标志(01)
// 偏向锁:23位线程ID|2位epoch|4位分代年龄|1位偏向模式|2位锁标志(01)
// 轻量级锁:30位指向栈中锁记录的指针|2位锁标志(00)
// 重量级锁:30位指向监视器锁的指针|2位锁标志(10)

2.2 对象锁与类锁的实战差异

对象锁作用于实例方法时,锁的是当前对象实例;作用于代码块时,锁的是指定对象。类锁则是锁定Class对象,影响所有实例的访问。这种差异在分布式环境下尤其需要注意:

public class OrderService {
    // 对象锁
    public synchronized void createOrder() {
        // 操作实例变量
    }
    
    // 类锁
    public static synchronized void updateConfig() {
        // 操作静态变量
    }
    
    public void process() {
        Object lock = new Object();
        synchronized(lock) { // 自定义对象锁
            // 临界区代码
        }
    }
}

在集群环境中,类锁只能保证单个JVM内的同步,需要配合分布式锁实现全局控制。而对象锁的粒度更细,适合保护实例级别的资源。

2.3 锁升级的底层机制

当多个线程竞争锁时,JVM会经历锁升级过程:

  1. 偏向锁:第一个访问线程通过CAS设置线程ID
  2. 轻量级锁:出现竞争时转换为指向栈中锁记录的指针
  3. 重量级锁:自旋超过阈值(默认10次)后升级为操作系统级别的互斥量

使用jol-core工具可以观察对象头变化:

// 添加Maven依赖
// <dependency>
//     <groupId>org.openjdk.jol</groupId>
//     <artifactId>jol-core</artifactId>
//     <version>0.16</version>
// </dependency>

public static void main(String[] args) {
    Object obj = new Object();
    System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    synchronized(obj) {
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    }
}

三、volatile的内存语义深度解析

3.1 可见性原理与内存屏障

volatile变量的写操作会插入StoreStore屏障和StoreLoad屏障,保证:

  1. 写操作前的所有普通写对其它处理器可见
  2. 禁止与后续volatile写重排序

读操作会插入LoadLoad屏障和LoadStore屏障,确保:

  1. 每次读取都从主内存获取最新值
  2. 禁止与前面的volatile读重排序

3.2 双重检查锁定的正确实现

经典的单例模式实现需要volatile防止指令重排序:

public class Singleton {
    private static volatile Singleton instance;
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // 非原子操作
                }
            }
        }
        return instance;
    }
}

没有volatile时,new Singleton()可能被重排序为:

  1. 分配内存空间
  2. 将引用指向内存空间(此时instance != null)
  3. 初始化对象

其他线程可能拿到未初始化的实例,导致NPE。


四、CAS机制的底层实现与ABA问题

4.1 Unsafe类的魔法操作

Java通过Unsafe类提供CAS原子操作,核心方法:

public final native boolean compareAndSwapObject(
    Object o, long offset, Object expected, Object x);

以AtomicInteger为例:

public class AtomicInteger {
    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); }
    }
    
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
}

4.2 ABA问题的解决方案

使用版本号戳记解决ABA问题:

AtomicStampedReference<Integer> atomicRef = 
    new AtomicStampedReference<>(100, 0);

int stamp = atomicRef.getStamp();
atomicRef.compareAndSet(100, 200, stamp, stamp+1); 

五、三大机制的对比与选型策略

5.1 性能对比基准测试

使用JMH测试不同场景下的性能:

@BenchmarkMode(Mode.Throughput)
@State(Scope.Thread)
public class LockBenchmark {
    private int counter = 0;
    private volatile int volatileCounter = 0;
    private AtomicInteger atomicCounter = new AtomicInteger(0);
    
    @Benchmark
    public void synchronizedIncrement() {
        synchronized(this) {
            counter++;
        }
    }
    
    @Benchmark
    public void atomicIncrement() {
        atomicCounter.incrementAndGet();
    }
}

测试结果可能显示:

  • 低竞争场景:CAS > 偏向锁 > volatile
  • 高竞争场景:锁升级后的重量级锁更稳定

5.2 选型决策树

  1. 需要原子性但无竞争 → volatile
  2. 简单同步且竞争不激烈 → synchronized
  3. 高并发计数 → AtomicLong+CAS
  4. 需要可中断锁 → ReentrantLock
  5. 状态标志 → volatile boolean
  6. 延迟初始化 → synchronized + volatile双检锁

六、现代并发模式下的演进

6.1 JDK8之后的改进

  1. StampedLock:乐观读锁提升读多写少场景性能
  2. LongAdder:分段CAS解决热点数据竞争
  3. VarHandle:替代Unsafe的安全内存访问

6.2 内存模型的最新发展

JEP 188提出的Java内存模型更新:

  • 新的Fences API(LoadFence/StoreFence)
  • 更精细的内存排序控制
  • 与C++内存模型更好的互操作性

正文到此结束
评论插件初始化中...
Loading...