ReentrantLock

ReentrantLock 源码分析

ReentrantLock 解决的问题:给 Java 一把比 synchronized 更可控的独占锁,可中断、可超时、可公平、可挂多个条件队列。
核心思路:锁本身只是门面,全部同步逻辑委托给 AQS 子类 Sync,用 AQS 的 volatile int state 记重入次数,state 从 0 CAS 到 1 即抢锁成功,抢不到就进 AQS 的 CLH 变种队列里 park 等待。

注意:JDK 15 起 ReentrantLock/AQS 被大改过,网上大量教程还是 JDK 8 的 nonfairTryAcquire + acquireQueued 版本,下面是新版真实结构(快路径 initialTryLock 与排队重试 tryAcquire 分离)。

代码块JAVA · 148 行收起展开
// 基于本地 JDK 源码 (D:/1ForCode/JAVA_Source, java.base, JDK 25), java.util.concurrent.locks.ReentrantLock
public class ReentrantLock implements Lock, java.io.Serializable {
    // ...
    private final Sync sync;    // 唯一字段:所有逻辑委托给 AQS 子类,公平/非公平只是换个 Sync 实现

    abstract static class Sync extends AbstractQueuedSynchronizer {
        // ...

        @ReservedStackAccess    // 预留栈空间执行完整个方法,防 StackOverflowError 把锁状态改到一半
        final boolean tryLock() {   // 无参 tryLock() 的实现:永远走非公平逻辑,公平锁调它照样插队
            Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, 1)) {         // 唯一竞争点:CAS 把 state 0 -> 1
                    setExclusiveOwnerThread(current);   // 必须先 CAS 成功再记 owner,反过来会被别的线程读到假 owner
                    return true;
                }
            } else if (getExclusiveOwnerThread() == current) {
                if (++c < 0) // overflow                // 重入 2^31-1 次才溢出,防御性检查
                    throw new Error("Maximum lock count exceeded");
                setState(c);                            // 只有持锁线程会写这里,独占写不需要 CAS
                return true;
            }
            return false;
        }

        abstract boolean initialTryLock();  // 公平/非公平的唯一分歧点:第一次抢锁前要不要看队列

        @ReservedStackAccess
        final void lock() {
            if (!initialTryLock())          // 快路径失败才进 AQS 的排队机器
                acquire(1);
        }

        @ReservedStackAccess
        final void lockInterruptibly() throws InterruptedException {
            if (Thread.interrupted())       // 进门先查中断:响应中断的优先级高于拿锁,哪怕锁现在就空着
                throw new InterruptedException();
            if (!initialTryLock())
                acquireInterruptibly(1);
        }

        @ReservedStackAccess
        final boolean tryLockNanos(long nanos) throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            return initialTryLock() || tryAcquireNanos(1, nanos);
        }

        @ReservedStackAccess
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (getExclusiveOwnerThread() != Thread.currentThread())
                throw new IllegalMonitorStateException();   // 没持锁就 unlock 直接炸:lock() 放 try 里面失败后走 finally,就是这个异常
            boolean free = (c == 0);
            if (free)
                setExclusiveOwnerThread(null);  // 先清 owner 再写 state
            setState(c);        // volatile 写放最后:把 owner=null 一起发布出去,对下一个加锁者 happens-before
            return free;        // 减到 0 才返回 true,release 才会去唤醒后继;重入未清零时不唤醒任何人
        }
        // ...
    }

    static final class NonfairSync extends Sync {
        // ...
        final boolean initialTryLock() {
            Thread current = Thread.currentThread();
            if (compareAndSetState(0, 1)) { // first attempt is unguarded   // 连 state 都不先读,上来盲 CAS:赌锁大概率空闲,省一次 volatile 读
                setExclusiveOwnerThread(current);
                return true;
            } else if (getExclusiveOwnerThread() == current) {
                int c = getState() + 1;
                if (c < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(c);
                return true;
            } else
                return false;
        }

        protected final boolean tryAcquire(int acquires) {  // 入队后每次被唤醒都调它
            if (getState() == 0 && compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;    // 没有重入分支:持锁线程不可能同时在队里等自己,重入在 initialTryLock 就返回了
            }
            return false;
        }
    }

    static final class FairSync extends Sync {
        // ...
        final boolean initialTryLock() {
            Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedThreads() && compareAndSetState(0, 1)) {  // 公平:队列里有人排队就不抢,直接去排队
                    setExclusiveOwnerThread(current);
                    return true;
                }
            } else if (getExclusiveOwnerThread() == current) {  // 重入不受公平性约束,否则持锁线程排队等自己 = 死锁
                if (++c < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(c);
                return true;
            }
            return false;
        }

        protected final boolean tryAcquire(int acquires) {
            if (getState() == 0 && !hasQueuedPredecessors() &&  // 入队后改查"有没有排在我前面的":自己已在队里,用 hasQueuedThreads 会把自己算进去永远失败
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
    }

    public ReentrantLock() {
        sync = new NonfairSync();   // 默认非公平:吞吐优先
    }

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    public void lock() {
        sync.lock();
    }

    public boolean tryLock() {
        return sync.tryLock();      // 即使是公平锁也插队;要不插队的非阻塞尝试,用 tryLock(0, TimeUnit)
    }

    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryLockNanos(unit.toNanos(timeout));
    }

    public void unlock() {
        sync.release(1);
    }

    public Condition newCondition() {
        return sync.newCondition(); // 一把锁可挂多个条件队列做精准唤醒;synchronized 只有一个 wait set
    }
    // ...
}

AQS 侧只需要看独占模式的三样东西:state、等待队列的 Node、以及 acquire/release 主干。

代码块JAVA · 143 行收起展开
// 基于本地 JDK 源码 (D:/1ForCode/JAVA_Source, java.base, JDK 25), java.util.concurrent.locks.AbstractQueuedSynchronizer
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer     // owner 线程存在这个父类里,exclusiveOwnerThread 字段
    implements java.io.Serializable {
    // ...

    // Node status bits, also used as argument and return values
    static final int WAITING   = 1;          // must be 1     // "我要 park 了,释放锁时请 unpark 我"
    static final int CANCELLED = 0x80000000; // must be negative   // 用符号位当取消标记,status < 0 一次比较判取消
    static final int COND      = 2;          // in a condition wait

    /** CLH Nodes */
    abstract static class Node {
        volatile Node prev;       // initially attached via casTail    // prev 链才是权威链,入队时随 CAS 一起建立
        volatile Node next;       // visibly nonnull when signallable  // next 是事后补的优化链,可能瞬时缺失,遍历要从 tail 沿 prev 走
        Thread waiter;            // visibly nonnull when enqueued
        volatile int status;      // written by owner, atomic bit ops by others
        // ...
    }

    private transient volatile Node head;   // 哨兵:head 代表"当前持锁者的位置",它的 next 才是第一个等待者
    private transient volatile Node tail;

    private volatile int state;     // 唯一同步状态。ReentrantLock 里 = 重入次数,Semaphore 里 = 剩余许可,语义由子类定义

    public final void acquire(int arg) {
        if (!tryAcquire(arg))       // 入队前再给一次机会,这是非公平锁的第二个插队点
            acquire(null, arg, false, false, false, 0L);    // 排队主循环,所有 acquire 变体共用这一个方法
    }

    final int acquire(Node node, int arg, boolean shared,
                      boolean interruptible, boolean timed, long time) {
        Thread current = Thread.currentThread();
        byte spins = 0, postSpins = 0;   // retries upon unpark of first thread
        boolean interrupted = false, first = false;
        Node pred = null;               // predecessor of node when enqueued
        // ...
        for (;;) {
            if (!first && (pred = (node == null) ? null : node.prev) != null &&
                !(first = (head == pred))) {    // 前驱是 head 才算队首,才有抢锁资格
                if (pred.status < 0) {
                    cleanQueue();           // predecessor cancelled    // 前驱取消了,摘掉取消节点后重来
                    continue;
                } else if (pred.prev == null) {
                    Thread.onSpinWait();    // ensure serialization     // 前驱还没完全入队,自旋等链稳定
                    continue;
                }
            }
            if (first || pred == null) {    // 只有队首(或还没入队的新线程)调 tryAcquire:队列中间的线程连试的资格都没有,不惊群
                boolean acquired;
                try {
                    if (shared)
                        acquired = (tryAcquireShared(arg) >= 0);
                    else
                        acquired = tryAcquire(arg);     // 回调到 NonfairSync/FairSync 的 tryAcquire
                } catch (Throwable ex) {
                    cancelAcquire(node, interrupted, false);    // 子类 tryAcquire 抛异常也要把节点摘掉,否则整条队列永久堵死
                    throw ex;
                }
                if (acquired) {
                    if (first) {
                        node.prev = null;   // 自己升级为新 head(哨兵),旧 head 断链帮 GC
                        head = node;
                        pred.next = null;
                        node.waiter = null;
                        if (shared)
                            signalNextIfShared(node);
                        if (interrupted)
                            current.interrupt();    // 不可中断模式:排队时吃掉的中断在拿到锁后补回标记
                    }
                    return 1;
                }
            }
            Node t;
            if ((t = tail) == null) {           // initialize queue    // 队列懒初始化:无竞争时一个节点都不分配
                if (tryInitializeHead() == null)
                    return acquireOnOOME(shared, arg);
            } else if (node == null) {          // allocate; retry before enqueue
                try {
                    node = (shared) ? new SharedNode() : new ExclusiveNode();
                } catch (OutOfMemoryError oome) {
                    return acquireOnOOME(shared, arg);
                }
            } else if (pred == null) {          // try to enqueue
                node.waiter = current;
                node.setPrevRelaxed(t);         // avoid unnecessary fence
                if (!casTail(t, node))          // 入队只 CAS tail 一个点
                    node.setPrevRelaxed(null);  // back out
                else
                    t.next = node;              // next 链事后补,所以它不可靠
            } else if (first && spins != 0) {
                --spins;                        // reduce unfairness on rewaits    // 被唤醒却被插队抢走了?先自旋几圈别急着再睡
                Thread.onSpinWait();
            } else if (node.status == 0) {
                node.status = WAITING;          // enable signal and recheck    // 先声明"我要睡了",下轮循环再试一次锁,最后才真 park
            } else {
                spins = postSpins = (byte)((postSpins << 1) | 1);   // 每被无效唤醒一次,下次自旋配额翻倍
                try {
                    long nanos;
                    if (!timed)
                        LockSupport.park(this);     // 线程真正阻塞在这一行
                    else if ((nanos = time - System.nanoTime()) > 0L)
                        LockSupport.parkNanos(this, nanos);
                    else
                        break;                      // 超时,跳出去走取消路径返回 0
                } catch (Error | RuntimeException ex) {
                    cancelAcquire(node, interrupted, interruptible); // cancel & rethrow
                    throw ex;
                }
                node.clearStatus();
                if ((interrupted |= Thread.interrupted()) && interruptible)
                    break;                          // 可中断模式:立刻跳出,cancelAcquire 返回负数,外层抛 InterruptedException
            }
        }
        return cancelAcquire(node, interrupted, interruptible);
    }

    public final boolean release(int arg) {
        if (tryRelease(arg)) {          // 回调 Sync.tryRelease:state 减到 0 才为 true
            signalNext(head);           // 只唤醒 head 的后继这一个线程
            return true;
        }
        return false;
    }

    private static void signalNext(Node h) {
        Node s;
        if (h != null && (s = h.next) != null && s.status != 0) {   // status == 0 说明对方还没准备睡,不用 unpark
            s.getAndUnsetStatus(WAITING);   // 原子清 WAITING 位,与 park 前"先置位再检查"的协议配对,防唤醒丢失
            LockSupport.unpark(s.waiter);
        }
    }

    public final boolean hasQueuedPredecessors() {
        Thread first = null; Node h, s;
        if ((h = head) != null && ((s = h.next) == null ||
                                   (first = s.waiter) == null ||
                                   s.prev == null))
            first = getFirstQueuedThread(); // retry via getFirstQueuedThread   // next 链瞬时不一致时,兜底从 tail 沿 prev 反向找队首
        return first != null && first != Thread.currentThread();   // 队首是自己 = 没有前驱,公平锁被唤醒后才能通过这关
    }
    // ...
}

原理串讲

拿默认的非公平锁走一遍完整链路。线程 T1 调 lock(),进 Sync.lock(),先走 NonfairSync.initialTryLock():不读 state 直接 compareAndSetState(0, 1),成功后 setExclusiveOwnerThread(T1),整个加锁就两步,队列、Node 什么都没碰。
这就是无竞争时 ReentrantLock 快的原因:懒到极致,连等待队列的 head 节点都是第一次有人排队时才建(tryInitializeHead)。

T2 这时也来 lock()initialTryLock 里 CAS 失败、owner 又是 T1,返回 false,进 AQS.acquire(1)
这里先再调一次 tryAcquire(第二个插队点),还是失败,进主循环 acquire(null, 1, false, false, false, 0L)
循环里依次完成:建 ExclusiveNodecasTail 入队、把 status 置为 WAITING、再绕一圈确认还是抢不到,最后 LockSupport.park(this) 睡死在这一行。
注意置 WAITING 和 park 之间隔了一整轮循环重试。为什么这么设计?因为 signalNext 只对 status != 0 的节点执行 unpark,如果 T2 先检查锁再置位睡觉,T1 可能恰好在”检查完、还没睡”的窗口里释放锁并跳过唤醒,T2 就永远睡下去了。
先声明”我要睡了”、再做最后一次检查、然后才真睡,这是标准的防唤醒丢失协议,和 signalNext 里的 getAndUnsetStatus(WAITING) 严格配对。

T1 干完活调 unlock(),进 AQS.release(1) 回调 Sync.tryRelease(1):先验证调用者就是 owner(不是就抛 IllegalMonitorStateException),state 减 1。
若 T1 是重入了两层的状态,这里减完还剩 1,返回 false,release 直接结束,谁也不唤醒,锁还在 T1 手里。
减到 0 时先 setExclusiveOwnerThread(null)setState(0),volatile 写放在最后一步,把清 owner 的动作一起发布出去。
然后 releasesignalNext(head),只 unpark head 的后继一个线程,也就是 T2。
为什么只唤醒一个而不是全部?因为独占锁一次只能有一个赢家,全唤醒就是惊群:一堆线程醒来抢一个 CAS,输家再集体睡回去,白付 n-1 次上下文切换。
head 哨兵设计保证了”谁该被唤醒”是 O(1) 可定位的。

T2 从 park 醒来,回到循环顶部,发现自己前驱就是 head,first == true,调 NonfairSync.tryAcquire,CAS 成功。
收尾动作是把自己升级成新 head:node.prev = null; head = node; pred.next = null,自己变成下一任哨兵。
这里还有个细节:非公平模式下 T2 醒来那一瞬间可能被路过的新线程插队抢走锁,此时 T2 不会立刻再 park,而是按 postSpins 翻倍的配额先自旋几圈(“reduce unfairness on rewaits”),用少量空转对冲非公平带来的重复挂起。

公平锁的差别只在两个 try 方法:initialTryLockhasQueuedThreads(),队里有任何人就不抢;入队后的 tryAcquire 换成 hasQueuedPredecessors(),只要求”我前面没人”。
为什么要用两个不同的检查?因为入队后自己就在队列里,继续用 hasQueuedThreads 会把自己算进去,队首线程永远无法通过检查,锁就没人能拿了。
另外重入分支在公平锁里也不检查队列,持锁线程重入必须无条件放行,否则它会去排队等自己释放,直接死锁。

设计取舍

  • 默认非公平:锁释放到被唤醒者真正运行之间有几微秒到几十微秒的窗口,让路过的线程直接插队能把这段窗口利用起来,吞吐显著更高;代价是极端竞争下排队者可能长时间抢不到。公平锁把每次唤醒变成严格 FIFO 交接,多一次队列检查外加大量上下文切换,吞吐可差一个数量级。
  • 无参 tryLock() 故意不检查公平性,公平锁上它也插队,这是文档明说的特性;想要遵守公平性的非阻塞尝试,用 tryLock(0, TimeUnit.SECONDS)
  • 重入靠 state 计数,lock 几次必须 unlock 几次;unlock() 放 finally 且 lock() 写在 try 之外,否则加锁失败也会走 finally 触发 IllegalMonitorStateException。
  • JDK 15+ 把重入检查收进 initialTryLock,排队后的 tryAcquire 不再判断重入:持锁线程不可能在队列里等自己,老版本每次队列重试都白查一次 owner。
  • 对比 synchronized:只在需要可中断、超时、公平、多 Condition 之一时才选 ReentrantLock;现代 JVM 下两者无竞争性能相当,synchronized 不用手动释放且有偏向 monitor 的 JIT 优化,能用就优先用。

延伸阅读