Java中关于多线程 ReentrantLock + Condition 的代码理解,逐步拆解,一文弄懂!
ReentrantLock + Condition 的代码理解
源于:https://liaoxuefeng.com/books/java/threading/synchronize/condition/index.html
源代码如下:
class TaskQueue {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private Queue<String> queue = new LinkedList<>();
public void addTask(String s) {
lock.lock();
try {
queue.add(s);
condition.signalAll();
} finally {
lock.unlock();
}
}
public String getTask() {
lock.lock();
try {
while (queue.isEmpty()) {
condition.await();
}
return queue.remove();
} finally {
lock.unlock();
}
}
}
使用 ReentrantLock + Condition 实现线程安全的任务队列
这段代码使用了 ReentrantLock + Condition 来实现一个线程安全的任务队列,解决了之前 synchronized + while空转 导致 CPU 100% 占用的问题。
1. 成员变量
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private Queue<String> queue = new LinkedList<>();
- lock:显式锁,用于代替
synchronized来控制并发。 - condition:条件对象,相当于
Object.wait()/notify()的替代品,但功能更强。 - queue:任务队列。
2. addTask(String s)
public void addTask(String s) {
lock.lock();
try {
queue.add(s);
condition.signalAll();
} finally {
lock.unlock();
}
}
- 先
lock.lock()加锁,确保线程安全。 - 往 queue 添加任务。
condition.signalAll():通知所有在condition.await()上等待的线程,“有新任务了,可以继续执行”。- finally 里 unlock(),防止死锁。
3. getTask()
public String getTask() {
lock.lock();
try {
while (queue.isEmpty()) {
condition.await();
}
return queue.remove();
} finally {
lock.unlock();
}
}
lock.lock():加锁,防止多个线程同时修改队列。while (queue.isEmpty()) { condition.await(); }- 如果队列空,则进入等待。
- condition.await() 会做两件事:
- 释放 lock,让
addTask()可以拿到锁执行。 - 当前线程阻塞(不会空转消耗CPU)。
- 释放 lock,让
- 当
addTask()调用condition.signalAll(),等待的线程会被唤醒,重新竞争锁。
return queue.remove();:取出并返回任务。
核心改进点:
- 之前的写法:
while (queue.isEmpty()) {}死循环,占用 CPU 100%,因为线程在不停地做条件检查。 - 现在的写法:
condition.await()让线程挂起,不消耗CPU,直到被signalAll()唤醒。
为什么要 while 而不是 if?
因为可能出现 虚假唤醒(spurious wakeup),线程被唤醒后,队列可能仍然是空的,所以需要再次检查 queue.isEmpty()。
符号简笔图:运行流程
符号约定
- 🔒 锁定:线程获取 Lock
- ⏳ 等待:线程挂起,等待条件 signal
- ✅ 唤醒:
condition.signalAll() - 📦 任务队列:LinkedList
步骤图
初始状态
[线程 A] ----> getTask()
[线程 B] ----> addTask()
任务队列: [ ] (空)
Lock: 🔓 (未锁)
线程 A 调用 getTask()
[线程 A] 获取 Lock 🔒
队列为空 -> 进入 while 循环
调用 condition.await()
↓
释放 Lock 🔓,挂起 ⏳,等待 signal
当前状态:
[线程 A] (等待)
任务队列: [ ] (空)
Lock: 🔓
线程 B 调用 addTask("X")
[线程 B] 获取 Lock 🔒
queue.add("X") -> 任务队列:[X]
调用 condition.signalAll() ✅ (通知等待线程)
释放 Lock 🔓
当前状态:
任务队列:[ X ]
[线程 A] (被唤醒)
线程 A 被唤醒
[线程 A] 重新竞争 Lock 🔒
条件满足 (队列不为空)
queue.remove() -> 获取 "X"
释放 Lock 🔓
返回 "X"
简图总结
getTask():
[获取锁🔒] -> [队列为空?] -> [await() ⏳ 等待 signal]
↑
|
addTask(): [获取锁🔒] -> [添加任务 📦] -> [signalAll ✅ 唤醒等待线程]
✅ 关键点直观解释
await()会释放锁并挂起,防止死锁。signalAll()唤醒所有等待线程,让它们重新尝试获取锁。- 任务队列的读写都必须在
lock下完成,保证线程安全。

浙公网安备 33010602011771号