ReentrantLock源码解析:从组合模式到AQS的协作奥秘

117 阅读3分钟

一、ReentrantLock的类结构:组合模式的艺术

ReentrantLock的核心代码围绕**AQS(AbstractQueuedSynchronizer)**展开,其类继承关系如下:

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;  // 关键:所有操作委托给Sync对象
    abstract static class Sync extends AbstractQueuedSynchronizer { /*...*/ }
    static final class NonfairSync extends Sync { /*...*/ }  // 非公平锁
    static final class FairSync extends Sync { /*...*/ }      // 公平锁
}

设计思想

  • 组合优于继承:ReentrantLock将锁的实现委托给内部类Sync,而非直接继承AQS,保持灵活性。
  • 策略模式:通过FairSyncNonfairSync实现不同锁策略,用户可通过构造函数选择。

二、核心方法剖析:lock()与unlock()

1. 非公平锁的lock()实现
// NonfairSync.lock()
final void lock() {
    if (compareAndSetState(0, 1))  // 直接尝试CAS抢锁(非公平性体现)
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);  // 进入AQS队列
}

// AQS.acquire()
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&      // 调用子类实现的tryAcquire()
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

关键点

  • 非公平性:新线程无需排队,直接尝试插队获取锁。
  • tryAcquire逻辑
    // NonfairSync.tryAcquire()
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
    
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {  // 再次尝试CAS
                setExclusiveOwnerThread(current);
                return true;
            }
        } else if (current == getExclusiveOwnerThread()) {  // 重入逻辑
            int nextc = c + acquires;
            if (nextc < 0) throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
    
2. 公平锁的tryAcquire()
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() &&  // 关键:检查是否有前驱节点等待
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 重入逻辑与非公平锁相同
    // ...
}

公平性保证hasQueuedPredecessors()检查队列中是否有其他线程等待,确保“先到先得”。


三、AQS源码逻辑:同步器的核心骨架

1. AQS的双向队列(CLH变体)
  • Node结构
    static final class Node {
        volatile Node prev;       // 前驱节点
        volatile Node next;       // 后继节点
        volatile Thread thread;   // 关联线程
        int waitStatus;           // 状态:CANCELLED(1), SIGNAL(-1), CONDITION(-2)
    }
    
  • 队列管理
    • 入队addWaiter()将线程包装为Node,通过CAS插入队尾。
    • 出队:头节点释放锁后,唤醒后继节点。
2. acquireQueued():自旋与阻塞
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {  // 前驱是头节点且抢锁成功
                setHead(node);                   // 当前节点设为头节点
                p.next = null;                   // 帮助GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) && // 检查是否需要阻塞
                parkAndCheckInterrupt())          // 调用LockSupport.park()
                interrupted = true;
        }
    } finally {
        if (failed) cancelAcquire(node);         // 异常处理
    }
}

自旋优化:线程在阻塞前多次尝试获取锁,减少上下文切换开销。

3. unlock()与锁释放
// ReentrantLock.unlock()
public void unlock() {
    sync.release(1);  // 调用AQS.release()
}

// AQS.release()
public final boolean release(int arg) {
    if (tryRelease(arg)) {        // 调用子类实现的tryRelease()
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);   // 唤醒后继节点
        return true;
    }
    return false;
}

// Sync.tryRelease()
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {                // 完全释放锁
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);                 // 无需CAS,只有持有锁的线程能调用release
    return free;
}

唤醒逻辑unparkSuccessor()找到下一个有效节点,调用LockSupport.unpark()唤醒线程。


四、AQS的设计哲学:模板方法模式

AQS通过模板方法模式将同步逻辑抽象为两类方法:

  1. 需子类实现
    • tryAcquire(int arg):尝试获取资源。
    • tryRelease(int arg):尝试释放资源。
  2. AQS实现
    • 线程排队、阻塞/唤醒、超时与中断处理。

优势

  • 复用性:AQS作为基类,可扩展为多种同步工具(如Semaphore、CountDownLatch)。
  • 关注点分离:子类只需关注资源状态管理,无需处理线程调度细节。

五、总结:ReentrantLock与AQS的协作全景

  1. ReentrantLock通过组合AQS实现锁功能,将并发控制的核心逻辑委托给Sync对象。
  2. AQS提供线程排队与调度框架,通过state和双向队列管理资源竞争。
  3. 公平性与非公平性是策略选择的结果,体现在tryAcquire()的实现差异。
  4. 重入性通过累加state实现,释放时需完全归零才会唤醒其他线程。

学习启示:理解AQS是掌握Java并发包的关键,其设计体现了“分离变与不变”的软件工程思想。