JavaGuide项目解析:深入理解ReentrantLock与AQS原理
前言
在Java并发编程中,锁机制是保证线程安全的重要手段。ReentrantLock作为Java并发包中提供的可重入锁,相比synchronized关键字提供了更灵活的锁控制能力。本文将深入分析ReentrantLock的实现原理,特别是它与AQS(AbstractQueuedSynchronizer)框架的关系,帮助开发者更好地理解Java并发锁机制。
ReentrantLock核心特性
基本特点
ReentrantLock是Java并发包中提供的可重入互斥锁,具有以下核心特性:
- 可重入性:同一个线程可以多次获取同一把锁
- 公平性选择:支持公平锁和非公平锁两种模式
- 灵活加锁:提供tryLock()、lockInterruptibly()等多种加锁方式
- 条件变量:支持多个Condition条件队列
与synchronized对比
| 特性 | ReentrantLock | synchronized | |---------------------|-----------------------------------|----------------------------| | 实现方式 | Java代码实现 | JVM内置实现 | | 锁获取方式 | 显式调用lock()/unlock() | 隐式获取和释放 | | 可中断 | 支持 | 不支持 | | 超时获取 | 支持 | 不支持 | | 公平性 | 可配置 | 非公平 | | 条件队列 | 支持多个 | 单个 | | 性能 | Java 6+优化后性能接近 | 优化后性能良好 |
AQS框架解析
AQS核心思想
AbstractQueuedSynchronizer(AQS)是Java并发包中锁机制的基石框架,其核心思想是:
- 状态管理:通过volatile int state表示同步状态
- 队列管理:使用CLH队列变体管理等待线程
- 模板方法:提供获取/释放锁的模板方法,具体实现由子类完成
AQS数据结构
AQS内部维护了一个双向队列,队列节点Node包含以下重要字段:
static final class Node {
volatile int waitStatus; // 节点状态
volatile Node prev; // 前驱节点
volatile Node next; // 后继节点
volatile Thread thread; // 节点关联线程
Node nextWaiter; // 条件队列后继节点
}
waitStatus状态值含义:
- SIGNAL(-1):后继节点需要被唤醒
- CANCELLED(1):节点因超时或中断被取消
- CONDITION(-2):节点在条件队列中
- PROPAGATE(-3):共享模式下传播状态
同步状态State
AQS使用volatile变量state表示同步状态:
private volatile int state;
对于ReentrantLock,state表示:
- 0:锁未被占用
-
0:锁被占用,数值表示重入次数
ReentrantLock与AQS的协作
类结构关系
ReentrantLock内部通过Sync类继承AQS:
- FairSync:公平锁实现
- NonfairSync:非公平锁实现
加锁流程分析
非公平锁加锁
final void lock() {
if (compareAndSetState(0, 1)) // 快速尝试获取锁
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // 进入AQS获取流程
}
AQS获取流程
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 子类实现的获取方法
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
非公平锁tryAcquire实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) { // 非公平地尝试获取
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;
}
线程入队过程
当tryAcquire失败时,线程会被加入等待队列:
- 创建节点:通过addWaiter()创建EXCLUSIVE模式节点
- 快速入队:尝试快速设置尾节点
- 完整入队:通过enq()方法确保节点正确入队
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node); // 完整入队流程
return node;
}
队列中线程的阻塞与唤醒
线程入队后会进入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; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) // 检查并阻塞线程
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire方法确保前驱节点状态正确:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) // 前驱节点会通知当前节点
return true;
if (ws > 0) { // 前驱节点已取消
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL); // 设置前驱为SIGNAL
}
return false;
}
解锁流程分析
unlock方法入口
public void unlock() {
sync.release(1);
}
AQS释放流程
public final boolean release(int arg) {
if (tryRelease(arg)) { // 子类实现的释放方法
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒后继节点
return true;
}
return false;
}
ReentrantLock的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;
}
唤醒后继节点
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); // 唤醒线程
}
公平锁与非公平锁区别
实现差异
公平锁在tryAcquire中增加了hasQueuedPredecessors检查:
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;
}
}
// ...重入逻辑相同
}
性能比较
- 非公平锁:吞吐量更高,但可能导致线程饥饿
- 公平锁:保证先来先服务,但上下文切换更多
总结
- ReentrantLock通过内部Sync类继承AQS实现锁功能
- AQS通过state变量和CLH队列管理同步状态
- 非公平锁会直接尝试获取锁,失败后才入队
- 公平锁严格遵循FIFO原则,先检查队列再获取
- 解锁时会唤醒队列中合适的等待线程
理解ReentrantLock和AQS的协作原理,有助于我们更好地使用并发工具,并在需要时实现自定义的同步器。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考