ReentrantLock 学习笔记
·
一、核心概述
ReentrantLock 是 Java JUC(java.util.concurrent)包下的可重入独占锁,基于 AQS(AbstractQueuedSynchronizer)实现,相比 synchronized 具备更灵活的锁控制能力(如可中断、可超时、公平 / 非公平锁切换),是并发编程中替代 synchronized 的核心工具。
二、核心 API
1. 构造方法
| 方法 | 说明 |
|---|---|
ReentrantLock() |
默认构造,创建非公平锁(性能更优,默认推荐) |
ReentrantLock(boolean fair) |
手动指定锁类型:- true:公平锁(按等待队列顺序获取锁,避免饥饿)- false:非公平锁(抢占式,可能插队) |
2. 核心操作方法
| 方法 | 说明 |
|---|---|
void lock() |
获取锁,若锁被占用则阻塞(不可中断) |
void lockInterruptibly() |
获取锁,支持线程中断(阻塞时可响应 interrupt ()) |
boolean tryLock() |
尝试获取锁,立即返回结果:- true:获取成功- false:获取失败(不阻塞) |
boolean tryLock(long timeout, TimeUnit unit) |
超时获取锁:在指定时间内尝试获取,超时 / 中断则返回 false |
void unlock() |
释放锁(必须与 lock 配对,建议放在 finally 中) |
Condition newCondition() |
创建条件变量,实现精准的线程等待 / 唤醒(替代 Object.wait/notify) |
boolean isLocked() |
判断锁是否被占用 |
boolean isFair() |
判断是否为公平锁 |
boolean isHeldByCurrentThread() |
判断当前线程是否持有该锁 |
int getHoldCount() |
获取当前线程持有该锁的重入次数 |
三、使用示例
1. 基础可重入使用
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockBasicDemo {
// 创建非公平锁(默认)
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
// 启动两个线程竞争锁
new Thread(ReentrantLockBasicDemo::doWork, "Thread-1").start();
new Thread(ReentrantLockBasicDemo::doWork, "Thread-2").start();
}
private static void doWork() {
lock.lock(); // 获取锁
try {
System.out.println(Thread.currentThread().getName() + " 获取锁,重入次数:" + lock.getHoldCount());
// 演示可重入:同一线程再次获取锁
reentrantMethod();
Thread.sleep(1000); // 模拟业务逻辑
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock(); // 释放锁,重入次数减1
System.out.println(Thread.currentThread().getName() + " 释放锁,重入次数:" + lock.getHoldCount());
}
}
private static void reentrantMethod() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 重入方法,重入次数:" + lock.getHoldCount());
} finally {
lock.unlock();
}
}
}
输出结果:
Thread-1 获取锁,重入次数:1
Thread-1 重入方法,重入次数:2
Thread-1 释放锁,重入次数:1
Thread-1 释放锁,重入次数:0
Thread-2 获取锁,重入次数:1
Thread-2 重入方法,重入次数:2
Thread-2 释放锁,重入次数:1
Thread-2 释放锁,重入次数:0
2. 公平锁 vs 非公平锁
import java.util.concurrent.locks.ReentrantLock;
public class FairVsUnfairLockDemo {
// 公平锁:new ReentrantLock(true)
private static final ReentrantLock fairLock = new ReentrantLock(true);
// 非公平锁:new ReentrantLock(false)
private static final ReentrantLock unfairLock = new ReentrantLock(false);
public static void main(String[] args) {
// 测试公平锁
System.out.println("=== 公平锁测试 ===");
for (int i = 0; i < 5; i++) {
new Thread(() -> acquireLock(fairLock), "Fair-Thread-" + i).start();
}
// 休眠2秒,确保公平锁测试完成
try { Thread.sleep(2000); } catch (InterruptedException e) {}
// 测试非公平锁
System.out.println("=== 非公平锁测试 ===");
for (int i = 0; i < 5; i++) {
new Thread(() -> acquireLock(unfairLock), "Unfair-Thread-" + i).start();
}
}
private static void acquireLock(ReentrantLock lock) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取到锁");
Thread.sleep(100); // 模拟业务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
核心差异:
- 公平锁:线程按等待顺序获取锁,输出顺序基本与线程启动顺序一致;
- 非公平锁:线程可能插队获取锁,输出顺序随机(性能更高,减少上下文切换)。
3. Condition 条件变量使用
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockConditionDemo {
private static final ReentrantLock lock = new ReentrantLock();
// 创建两个条件变量:等待队列、通知队列
private static final Condition waitCondition = lock.newCondition();
private static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
// 等待线程
Thread waitThread = new Thread(() -> {
lock.lock();
try {
// 条件不满足则等待
while (!flag) {
System.out.println(Thread.currentThread().getName() + " 等待条件满足");
waitCondition.await(); // 释放锁并等待
}
System.out.println(Thread.currentThread().getName() + " 条件满足,执行业务");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}, "WaitThread");
// 通知线程
Thread notifyThread = new Thread(() -> {
lock.lock();
try {
flag = true;
System.out.println(Thread.currentThread().getName() + " 修改条件,唤醒等待线程");
waitCondition.signal(); // 唤醒单个等待线程
} finally {
lock.unlock();
}
}, "NotifyThread");
waitThread.start();
Thread.sleep(1000); // 确保等待线程先执行
notifyThread.start();
}
}
输出结果:
WaitThread 等待条件满足
NotifyThread 修改条件,唤醒等待线程
WaitThread 条件满足,执行业务
四、底层原理
1. 核心依赖:AQS(抽象队列同步器)
ReentrantLock 内部封装了 Sync 抽象类(继承 AQS),并实现了两个子类:
NonfairSync:非公平锁实现;FairSync:公平锁实现。
AQS 核心结构:
- 状态变量:
state(int 类型),0 表示锁空闲,>0 表示锁被持有(数值 = 重入次数); - CLH 队列:双向链表,存储等待获取锁的线程(Node 节点);
- 独占模式:ReentrantLock 是独占锁,同一时刻仅一个线程持有锁。
2. 加锁流程(非公平锁为例)

3. 公平锁 vs 非公平锁核心差异
- 非公平锁:加锁时直接 CAS 抢占,失败后再入队(可能插队,性能高);
- 公平锁:加锁前先检查 CLH 队列是否有等待线程,若有则直接入队,不抢占(严格按顺序,性能低但公平)。
4. 解锁流程
- 调用
unlock()时,state-1; - 若
state减至 0,释放独占线程,唤醒 CLH 队列头节点的后继线程; - 若
state> 0(重入未释放完),仅更新 state,不释放锁。
5. 可重入实现原理
加锁时通过 Thread.currentThread() == getExclusiveOwnerThread() 判断当前线程是否为持有锁的线程,若是则直接将 state+1,无需重新竞争;解锁时逐次 state-1,直到 state=0 才真正释放锁。
五、与 synchronized 对比
| 特性 | ReentrantLock | synchronized |
|---|---|---|
| 可重入 | 支持 | 支持 |
| 锁类型 | 公平 / 非公平可配置 | 非公平锁 |
| 中断性 | 支持(lockInterruptibly ()) | 不支持 |
| 超时获取 | 支持(tryLock (timeout)) | 不支持 |
| 条件变量 | 支持多个(newCondition ()) | 仅一个(对象监视器) |
| 释放方式 | 手动 unlock(必须放 finally) | 自动释放(出作用域) |
总结
- ReentrantLock 是基于 AQS 的可重入独占锁,核心 API 涵盖锁的获取(lock/tryLock)、释放(unlock)、条件变量(Condition)等,使用时需手动保证 unlock 与 lock 配对;
- 底层通过 AQS 的 state 变量记录重入次数,CLH 队列管理等待线程,公平 / 非公平锁的核心差异在于加锁时是否直接抢占;
- 相比 synchronized,ReentrantLock 更灵活(支持中断、超时、多条件变量),但需手动释放锁,适合复杂并发场景。
更多推荐




所有评论(0)