Skip to content

AQS 独占模式源码解析

一、AQS基础概念

  • AbstractQueuedSynchronizer(抽象队列同步器)
  • CLH队列变种实现
  • volatile int state 核心状态
  • 独占模式与共享模式区别

二、Node节点结构

java
static final class Node {
    // 节点状态字段
    volatile int waitStatus;
    // 队列指针
    volatile Node prev, next;
    // 线程引用
    volatile Thread thread;
}

三、acquire方法全流程解析(深度注解版)

java
/**
 * 独占模式获取同步状态的完整流程
 * 包含尝试获取、入队等待、线程阻塞等复合操作
 * 
 * @param arg 请求的同步状态量(如锁的计数)
 */
public final void acquire(int arg) {
    // 核心执行逻辑包含两个关键阶段:
    if (!tryAcquire(arg) &&  // 快速尝试获取失败(非阻塞)
        // 队列化获取过程(阻塞语义):
        acquireQueued(  
            addWaiter(Node.EXCLUSIVE), // 将当前线程包装为独占节点加入等待队列
            arg))                      // 实际请求的同步状态量
        // 特殊处理:若在等待过程中发生中断,进行最终中断标记设置
        selfInterrupt();              // Thread.currentThread().interrupt()
}

四、关键方法深度剖析

1. tryAcquire方法(以ReentrantLock为例)

java
/**
 * 独占锁获取尝试实现(ReentrantLock核心逻辑)
 * 
 * 执行流程包含两个主要分支:
 * - 首次获取锁:当state=0时,通过CAS+公平性检查获取锁
 * - 可重入获取:已持有锁的线程再次获取时更新重入计数
 * 
 * @param acquires 请求获取的同步状态量(通常为1)
 * @return 是否成功获取锁
 */
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();  // 获取当前同步状态
    
    // 首次获取锁场景
    if (c == 0) {
        // 公平性保障机制:
        // 检查CLH队列中是否存在等待节点(避免插队)
        if (!hasQueuedPredecessors() && 
            compareAndSetState(0, acquires)) {  // 原子化状态更新
            
            // 成功获取锁后设置独占线程
            setExclusiveOwnerThread(current);
            
            // 调试信息:可添加日志记录
            // System.out.println(current.getName() + " acquired lock");
            
            return true;
        }
    } 
    
    // 可重入获取场景
    else if (current == getExclusiveOwnerThread()) {
        // 计算新的重入次数
        int nextc = c + acquires;
        
        // 安全边界检查:防止int溢出导致的状态异常
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        
        // 更新同步状态(非原子操作,因为此时线程已持有锁)
        setState(nextc);
        
        // 调试信息:可添加日志记录
        // System.out.println(current.getName() + " re-acquired lock, count: " + nextc);
        
        return true;
    }
    
    // 锁已被其他线程占用或竞争失败
    return false;
}

2. acquireQueued方法核心循环(深度注解版)

java
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;  // 标记是否发生异常中断
    try {
        boolean interrupted = false;  // 记录线程是否被中断
        
        // 自旋等待获取同步状态的核心循环
        for (;;) {
            // 获取当前节点的前驱节点
            final Node p = node.predecessor();
            
            // 当且仅当:
            // 1. 当前节点是head的直接后继节点
            // 2. 并且成功获取到同步状态(tryAcquire成功)
            if (p == head && tryAcquire(arg)) {
                // 成功获取锁后的清理操作:
                setHead(node);      // 将当前节点设置为头节点
                p.next = null;      // 原头节点置空,帮助GC回收
                failed = false;     // 标记获取成功
                return interrupted; // 返回中断标志(可能被其他线程中断过)
            }
            
            // 判断是否需要进入阻塞状态:
            // shouldParkAfterFailedAcquire:检查并更新节点状态
            // parkAndCheckInterrupt:若需阻塞则执行park,并返回中断状态
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;  // 更新中断标记
        }
    } finally {
        // 异常退出时的清理逻辑:
        // 比如线程被中断、抛出异常等情况
        if (failed)
            cancelAcquire(node);   // 取消该节点的获取请求
    }
}

五、线程阻塞与唤醒机制

1. 底层实现原理

AQS通过 LockSupport.park()unpark() 实现线程调度,相较于传统 synchronized 的 wait/notify 机制,具有以下优势:

  • 无需持有对象监视器即可阻塞线程
  • 精确控制线程唤醒(unpark(Thread))
  • 基于 UNSAFE 类实现,避免竞态条件问题

2. waitStatus状态管理

同步队列中的每个 Node 节点维护线程状态字段 waitStatus,定义如下:

  • CANCELLED (1):节点线程被取消,进入终态
  • SIGNAL (-1):后续节点需被唤醒
  • CONDITION (-2):节点等待在条件变量上
  • PROPAGATE (-3):共享模式下传播唤醒状态

典型状态转换场景:

触发动作状态变化说明
获取锁失败初始化 → SIGNAL标记后续线程可被唤醒
当前线程被中断SIGNAL → CANCELLED清理无效节点
唤醒后检查状态SIGNAL → 0成功获取锁后重置状态
共享模式传播唤醒PROPAGATE → 0确保唤醒操作向后传递

3. 中断响应策略

AQS提供两种中断处理方式:

  • acquire(int arg)
    忽略中断请求,仅在获取锁后更新中断标志位
  • acquireInterruptibly(int arg)
    直接抛出 InterruptedException,适用于需及时响应中断的场景

六、release方法解析

java
public final boolean release(int arg) {
    // 1. 尝试释放同步状态(由子类实现)
    if (tryRelease(arg)) {
        Node h = head;
        // 2. 如果头节点存在且等待状态非0(说明可能有后续等待线程需要唤醒)
        if (h != null && h.waitStatus != 0)
            // 3. 唤醒后继节点中的线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}

1. tryRelease实现(ReentrantLock)深度解析

java
/**
 * 独占锁释放核心方法(可重入语义支持)
 * 
 * @param releases 请求释放的同步状态量
 * @return 是否完全释放了占用权(state == 0)
 */
protected final boolean tryRelease(int releases) {
    // 计算新的同步状态值
    int c = getState() - releases;
    
    // 安全检查:确保当前线程是锁持有者
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException("非持有锁线程尝试释放");

    boolean free = false;
    
    // 可重入退出逻辑:当state递减到0时才真正释放锁
    if (c == 0) {
        free = true;  // 标记锁已释放
        setExclusiveOwnerThread(null);  // 清除线程占有标识
    }
    
    // 更新同步状态(可能为重入释放场景)
    setState(c);
    
    // 返回释放结果:
    // - 可重入场景:返回false,锁仍被持有
    // - 完全释放:返回true,资源可供其他线程竞争
    return free;
}

七、设计模式与性能优化

  • 模板方法模式应用
  • CAS+自旋+阻塞的优化策略
  • 队列维护的原子性保证
  • 伪共享避免技巧