将用一家"线程银行"的故事,带你彻底理解ReentrantLock的源码实现。这家银行有一个VIP金库,使用了一套叫 AQS(Abstract Queued Synchronizer) 的智能锁系统,这正是ReentrantLock的核心!
🏦 故事背景:线程银行的VIP金库
想象一家银行:
- VIP金库 = 共享资源
- 客户 = 线程(Thread)
- 银行经理 = ReentrantLock
- 排队系统 = AQS(抽象队列同步器)
金库使用一套智能锁系统,包含两种模式:
- 非公平模式(默认):新客户可以插队
- 公平模式:严格先来后到
🔧 核心组件:银行智能锁系统
📜 银行设施规划图(源码结构)
java
public class ReentrantLock implements Lock {
private final Sync sync; // 锁系统的核心大脑
// 同步器基类(银行基础系统)
abstract static class Sync extends AbstractQueuedSynchronizer {
// 实现锁的核心方法
abstract void lock();
final boolean nonfairTryAcquire(int acquires) { /*...*/ }
protected final boolean tryRelease(int releases) { /*...*/ }
}
// 非公平模式系统
static final class NonfairSync extends Sync {
final void lock() { /*...*/ }
protected final boolean tryAcquire(int acquires) { /*...*/ }
}
// 公平模式系统
static final class FairSync extends Sync {
final void lock() { /*...*/ }
protected final boolean tryAcquire(int acquires) { /*...*/ }
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
}
🧩 组件功能解析
| 组件 | 银行比喻 | 技术作用 |
|---|---|---|
ReentrantLock | 银行经理 | 对外提供锁服务 |
Sync | 基础锁系统 | AQS的定制实现 |
NonfairSync | 允许插队的业务系统 | 非公平锁实现 |
FairSync | 严格排队的业务系统 | 公平锁实现 |
AQS | 排队管理系统 | 管理线程队列和状态 |
state | 金库使用计数牌 | 0=空闲,>0=被占用次数 |
CLH队列 | 客户等候区 | 存储等待线程的双向链表 |
🔐 上锁流程:客户进入金库
场景1:非公平模式(默认)
java
// NonfairSync.lock()
final void lock() {
// 第一步:尝试直接插队获取金库
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // 加入排队系统
}
// AQS.acquire()
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
🎬 故事发展:
- 新客户A到达金库
- 尝试插队:直接查看金库是否空闲(state=0)
- 成功:立即进入,state设为1
- 失败:去排队区取号等待
场景2:公平模式
java
// FairSync.lock()
final void lock() {
acquire(1); // 直接排队,不尝试插队
}
// FairSync.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;
}
}
// 重入锁逻辑(同非公平)
else if (current == getExclusiveOwnerThread()) {
// ...
}
return false;
}
🎬 故事发展:
- 新客户B到达金库
- 直接取号排队,不尝试插队
- 只有当自己是队首且金库空闲时才能进入
🔍 深入AQS排队系统源码
🏗️ AQS核心结构
java
public abstract class AbstractQueuedSynchronizer {
// 队首节点(虚节点)
private transient volatile Node head;
// 队尾节点
private transient volatile Node tail;
// 同步状态:0=空闲,>0=占用次数
private volatile int state;
// 排队节点
static final class Node {
volatile Node prev; // 前驱节点
volatile Node next; // 后继节点
volatile Thread thread; // 排队的线程
int waitStatus; // 等待状态
}
}
📥 加入排队队列:addWaiter()
java
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) { // 自旋直到成功
Node oldTail = tail;
if (oldTail != null) {
// 设置新节点的前驱
node.setPrevRelaxed(oldTail);
// CAS更新队尾
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
}
} else {
initializeSyncQueue(); // 初始化队列
}
}
}
😴 队列中等待:acquireQueued()
java
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) { // 自旋
final Node p = node.predecessor();
// 如果前驱是头节点(自己是第二个)
if (p == head && tryAcquire(arg)) {
setHead(node); // 获取成功,自己成为头节点
p.next = null; // 帮助GC
return interrupted;
}
// 检查是否需要阻塞
if (shouldParkAfterFailedAcquire(p, node))
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
🛌 线程阻塞:parkAndCheckInterrupt()
java
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); // 阻塞当前线程
return Thread.interrupted(); // 返回中断状态
}
🔓 解锁流程:客户离开金库
java
public void unlock() {
sync.release(1); // 释放一个占用计数
}
// AQS.release()
public final boolean release(int arg) {
if (tryRelease(arg)) { // 尝试释放
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); // 更新状态
return free;
}
⚡ 唤醒后续节点:unparkSuccessor()
java
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 清除状态
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
// 从后向前找有效节点(处理取消的节点)
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0) s = t;
}
if (s != null)
LockSupport.unpark(s.thread); // 唤醒线程
}
🔄 重入锁实现原理
当同一个客户多次进入金库:
java
// NonfairSync.nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 尝试获取锁...
}
// 重入关键:当前线程已持有锁
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires; // 增加计数
if (nextc < 0) throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
- state记录重入次数:每lock()一次+1,unlock()一次-1
- 完全释放条件:state归0时才释放锁
⚖️ 公平 vs 非公平实现差异
公平锁的额外检查
java
public final boolean hasQueuedPredecessors() {
Node h, s;
if ((h = head) != null) {
if ((s = h.next) == null || s.thread != Thread.currentThread())
return true; // 存在有效后继节点
}
return false;
}
| 场景 | 非公平锁 | 公平锁 |
|---|---|---|
| 新线程获取锁 | 可直接插队尝试获取 | 必须排队 |
| 吞吐量 | 更高(减少线程切换) | 略低 |
| 饥饿问题 | 可能发生 | 不会发生 |
| 实现复杂度 | 简单 | 需额外检查队列 |
| 适用场景 | 大多数高并发场景 | 严格要求公平性的场景 |
🧠 AQS 队列状态变化图
💡 技术总结
ReentrantLock 实现三大支柱:
-
AQS(AbstractQueuedSynchronizer) :
- CLH变体队列管理等待线程
state变量表示锁状态- 提供
acquire/release模板方法
-
CAS(Compare and Swap) :
- 实现无锁状态更新
- 保证
state和队列操作的原子性 - 关键方法:
compareAndSetState()
-
LockSupport:
- 线程阻塞(
park())和唤醒(unpark())的底层机制 - 比
Object.wait()/notify()更灵活
- 线程阻塞(
设计精髓:
- 模板方法模式:AQS提供骨架,子类实现具体策略
- 可重入设计:通过
state计数支持重复加锁 - 双模式支持:公平/非公平策略可配置
- 高性能队列:CLH变体减少锁竞争
🌟 一句话记住:
ReentrantLock = AQS队列管理 + CAS原子操作 + LockSupport线程控制
就像银行的智能VIP金库系统,既高效又公平地管理着线程访问!
掌握了这些原理,你就理解了Java并发包中最核心的锁实现机制!