概述

在Java并发编程的工具箱中,ReentrantLock 是最基础、最常用且最灵活的显式锁实现。作为 synchronized 关键字的强大替代品,它不仅提供了 可重入性(Reentrancy)、公平/非公平策略选择可中断等待超时获取 以及 多条件变量绑定 等高级特性,更以其清晰的代码结构成为理解 AbstractQueuedSynchronizer(AQS)框架的最佳入口。

本文将带你深入 ReentrantLock 的源码核心,从其基于 AQS 的整体架构、独占模式的实现细节、公平与非公平策略的差异,到条件变量(Condition)的工作原理,并结合云原生可观测性、虚拟线程(Project Loom)等2026年的技术趋势,探讨其未来的演进方向。

文章被收录于专栏云时代Java开发:原理、实战与优化


第一章:设计哲学——为何需要 ReentrantLock?

1.1 synchronized 的局限

尽管 synchronized 在 JVM 层面经过了大量优化(如偏向锁、轻量级锁、重量级锁),但它仍存在一些固有局限:

  • 不可中断:线程在等待锁时无法响应中断;
  • 不可超时:无法设置获取锁的最大等待时间;
  • 单一条件:每个对象只有一个内置的 wait/notify 队列,难以实现复杂的线程协调;
  • 非公平性固定:无法选择公平或非公平的获取策略。

1.2 ReentrantLock 的核心优势

ReentrantLock 作为 JUC 包中的基石,完美解决了上述问题:

  • 可重入:同一线程可多次获取同一把锁,内部维护一个 持有计数器
  • API 灵活:提供 lock(), unlock(), tryLock(), lockInterruptibly() 等多种获取方式;
  • 公平可选:通过构造函数指定是否为公平锁;
  • 多条件变量:通过 newCondition() 可创建多个独立的等待队列。

第二章:源码全景——基于AQS的架构

2.1 整体类结构

ReentrantLock 的设计是 模板方法模式委托模式 的典范:

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync; // 核心同步器

    // 抽象内部类,继承自AQS
    abstract static class Sync extends AbstractQueuedSynchronizer {
        // ...
    }

    // 非公平锁实现
    static final class NonfairSync extends Sync { /* ... */ }

    // 公平锁实现
    static final class FairSync extends Sync { /* ... */ }
}
  • Sync:抽象基类,定义了获取/释放锁的骨架;
  • NonfairSync / FairSync:具体策略实现,分别对应非公平和公平模式。

2.2 AQS 状态的含义

ReentrantLock 中,AQS 的 state 字段具有明确的语义:

  • state = 0:锁未被任何线程持有;
  • state > 0:锁已被持有,其值表示 重入次数
  • 独占模式ReentrantLock 仅使用 AQS 的 独占模式(Exclusive Mode)。

第三章:核心流程深度剖析

3.1 非公平锁:NonfairSync.lock()

非公平锁是 ReentrantLock 的默认实现,追求极致性能:

final void lock() {
    if (compareAndSetState(0, 1)) // 尝试直接CAS获取锁
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1); // 失败则进入AQS标准获取流程
}
  • 快速路径:新来的线程不检查队列,直接尝试 CAS 获取锁,实现“插队”;
  • 优势:在低竞争场景下,避免了入队/出队的开销,吞吐量极高。
tryAcquire 实现
protected final boolean tryAcquire(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) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

3.2 公平锁:FairSync.lock()

公平锁严格遵循 FIFO 原则,避免线程饥饿:

final void lock() {
    acquire(1); // 直接进入AQS标准获取流程
}

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;
        }
    } else if (current == getExclusiveOwnerThread()) {
        // ... 可重入逻辑,同非公平锁
    }
    return false;
}
  • hasQueuedPredecessors():若同步队列中有等待节点,则当前线程必须入队等待;
  • 代价:每次获取都需遍历队列头,性能略低于非公平锁。

3.3 锁的释放:unlock()

释放逻辑在 Sync 中统一实现,与公平性无关:

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

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);
    return free; // 若为true,AQS会唤醒后继节点
}

第四章:条件变量(Condition)支持

ReentrantLock 通过 newCondition() 提供了比 Object.wait/notify 更强大的线程协调能力。

4.1 ConditionObject 的内部结构

Condition 的实现是 AQS 的内部类 ConditionObject

  • 单向条件队列:与 AQS 的双向同步队列分离;
  • 节点类型Node 节点在条件队列中以 nextWaiter 链接。

4.2 await() 与 signal() 流程

  • await()

    1. 将当前线程封装为 Node,加入条件队列;
    2. 释放持有的 ReentrantLock
    3. 阻塞线程,等待被 signal 唤醒;
    4. 唤醒后,重新竞争 ReentrantLock
  • signal()

    1. 从条件队列头取出一个 Node
    2. 将其转移到 AQS 同步队列尾部;
    3. 由 AQS 负责后续的唤醒与锁竞争。

⚠️ 关键约束:调用 await/signal 前,线程必须持有 ReentrantLock,否则抛出 IllegalMonitorStateException


第五章:云原生与虚拟线程时代的挑战与演进

5.1 云原生可观测性增强

(1)分布式追踪集成
  • 现状:可通过 getOwner() 获取锁持有者线程;
  • 演进:扩展为返回 持有者上下文(包含 TraceID、SpanID),便于在 OpenTelemetry 中追踪锁竞争链路。
(2)Metrics 监控
  • 演进:集成 Micrometer,暴露 lockHoldTime, lockContentionRate, queueLength 等指标,助力 SRE 团队进行性能分析。

5.2 Project Loom 与虚拟线程

Project Loom 的 虚拟线程(Virtual Threads)对 ReentrantLock 提出了新的挑战:

(1)平台线程身份的模糊化
  • 挑战set/getExclusiveOwnerThread() 存储的是 平台线程(Carrier Thread),而多个虚拟线程可映射到同一个平台线程;
  • 风险:破坏可重入语义,导致 IllegalMonitorStateException
(2)演进方向
  • Continuation 感知:内部状态改用 Continuation IDVirtual Thread ID 标识持有者;
  • 结构化并发集成:与 StructuredTaskScope 协同,在 Scope 结束时自动释放所有锁,防止资源泄漏。

5.3 AI Agent 时代的智能锁管理

  • 场景:AI Agent 分析应用负载,动态调整锁策略;
  • 演进ReentrantLock 提供 自适应模式 API,如 switchToFairModeIfContentionHigh(),在检测到高竞争时自动切换至公平模式。

结语:显式锁的典范,AQS的启蒙之钥

ReentrantLock 以其 简洁的 API强大的功能清晰的源码结构,成为 Java 并发编程的必修课。它不仅是 Doug Lea “Simple, Correct, Fast” 工程哲学的杰出代表,更是无数高并发应用背后可靠的守护者。

在云原生、虚拟线程与 AI 驱动的 2026 年,ReentrantLock 的核心价值——提供灵活、可靠、高效的线程同步机制——依然坚如磐石。而它的实现细节,正随着技术浪潮不断演进,以适应更复杂、更智能的新时代需求。

你认为 ReentrantLock 在虚拟线程时代最大的挑战是什么?是线程身份的重新定义,还是全新的锁模型?欢迎在评论区分享你的见解!如果觉得本文助你深入理解 ReentrantLock,记得点赞、收藏,并转发给团队伙伴——一起构建更强大、更可靠的并发系统!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐