JMM 与 AQS、LockSupport、volatile、synchronized 的关系

43 阅读4分钟

一、整体关系框架

Java 内存模型(JMM)是并发编程的底层规则,定义了多线程环境下共享变量的访问方式。 volatilesynchronized 是 JMM 规则的具体实现手段,而 AQSLockSupport 是基于 JMM 和这些关键字构建的高并发工具。 以下是它们的协作关系:

JMM(规则层)
├── volatile(可见性、有序性)
├── synchronized(原子性、可见性、有序性)
├── AQS(基于 volatile 和 CAS 的同步框架)
└── LockSupport(基于 JMM 的线程阻塞与唤醒)

二、JMM 的核心规则

JMM 通过以下规则约束多线程行为:

  1. 可见性:线程对共享变量的修改对其他线程可见。
  2. 有序性:禁止指令重排序(通过内存屏障)。
  3. 原子性:特定操作(如锁、CAS)的不可分割性。

三、volatile 与 JMM

1. 作用

  • 可见性:强制读写直接操作主内存。
  • 有序性:插入内存屏障禁止指令重排序。

2. 实现原理

  • 写操作:在写后插入 StoreStoreStoreLoad 屏障。
  • 读操作:在读前插入 LoadLoadLoadStore 屏障。

3. 典型应用

  • 状态标志位(如 volatile boolean flag)。
  • 双重检查锁定(DCL 单例模式)。

四、synchronized 与 JMM

1. 作用

  • 原子性:同步代码块的互斥执行。
  • 可见性:锁释放时强制刷新工作内存到主内存。
  • 有序性:同步块内禁止指令重排序。

2. 内存语义

  • 加锁:清空工作内存,从主内存加载共享变量。
  • 解锁:将工作内存的修改刷新到主内存。

3. 典型应用

  • 保护临界区(如计数器递增)。
  • 实现互斥锁(如 ReentrantLock 的底层依赖)。

五、AQS 与 JMM

1. 核心依赖

  • volatile state:AQS 通过 volatile int state 管理同步状态,依赖 JMM 的可见性规则。
  • CAS 操作:通过 Unsafe.compareAndSwapXXX 实现原子性状态更新。

2. 同步机制

  • 等待队列:使用 CLH 队列管理阻塞线程,依赖 LockSupport 的 park()unpark()

  • 锁获取与释放

    // AQS 中的模板方法
    protected boolean tryAcquire(int arg) { /* 依赖 CAS 和 volatile state */ }
    protected boolean tryRelease(int arg) { /* 修改 state 并唤醒后继线程 */ }
    

3. 典型应用

  • ReentrantLockSemaphoreCountDownLatch 的底层实现。

六、LockSupport 与 JMM

1. 作用

  • 线程阻塞与唤醒:提供 park()unpark() 的底层支持。
  • 精准控制:可唤醒指定线程,避免 notify() 的随机性。

2. 实现依赖

  • 许可证机制:通过原子变量管理线程的阻塞状态。
  • happens-before 规则unpark() 操作对 park() 的可见性保证。

3. 典型应用

  • AQS 的等待队列管理。
  • 自定义线程协作(如替代 wait()/notify())。

七、协作关系示例

1. ReentrantLock 的工作流程

  1. 加锁

    • 通过 CAS 修改 volatile state(原子性、可见性)。
    • 若失败,线程加入 AQS 队列并调用 LockSupport.park() 阻塞(依赖 JMM 的可见性保证)。
  2. 解锁

    • 修改 volatile state,唤醒队列中的下一个线程(LockSupport.unpark())。

2. 双重检查锁定(DCL)

public class Singleton {
    private static volatile Singleton instance; // 依赖 volatile 的可见性与有序性
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) { // 依赖 synchronized 的原子性
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • volatile:禁止指令重排序(防止返回未初始化对象)。
  • synchronized:保证实例化代码的原子性。

八、对比与选择

组件解决的问题适用场景性能特点
volatile可见性、有序性状态标志、DCL 单例轻量级,无锁
synchronized原子性、可见性、有序性临界区保护、简单同步重量级,JVM 优化后性能提升
AQS复杂同步需求自定义锁、信号量、栅栏灵活,支持公平/非公平锁
LockSupport精准线程阻塞/唤醒AQS 队列管理、替代 wait/notify底层控制,无锁

九、总结

1. 层级关系

  • JMM:定义内存访问规则(可见性、有序性、原子性)。
  • volatile/synchronized:基于 JMM 的关键字实现。
  • AQS/LockSupport:基于 JMM 和关键字的高并发工具。

2. 协作逻辑

  • JMM 是基石:所有并发工具的行为均遵循其规则。
  • volatile 和 synchronized 是基础工具:直接解决可见性、有序性、原子性问题。
  • AQS 和 LockSupport 是高级工具:利用前者构建复杂同步机制。

3. 开发建议

  • 简单同步:优先使用 synchronizedvolatile
  • 复杂需求:使用 AQS 构建自定义同步器(如限流器、分布式锁)。
  • 精准控制:用 LockSupport 替代 wait()/notify()

通过理解这些组件的协作关系,开发者可以更高效地设计线程安全程序,避免内存可见性、指令重排序等并发陷阱。