本文目录导读:
在并发编程中,Condition(条件变量)是实现精确唤醒等待线程的核心机制,它解决了 wait/notify 无法指定唤醒特定线程的缺陷。
核心概念
Condition 将锁与等待/通知机制解耦:
- 每个
Condition对象维护一个独立的等待队列 - 线程可以等待特定条件(condition.await())
- 其他线程可以唤醒特定条件上的线程(condition.signal())
Java中的实现:Lock + Condition
基本用法
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BoundedBuffer {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition(); // 不满条件
private final Condition notEmpty = lock.newCondition(); // 不空条件
private final Object[] items = new Object[100];
private int putIndex, takIndex, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
// 当缓冲区满时,等待"不满"条件
while (count == items.length) {
notFull.await();
}
items[putIndex] = x;
if (++putIndex == items.length) putIndex = 0;
count++;
// 唤醒等待"不空"条件的线程
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
// 当缓冲区空时,等待"不空"条件
while (count == 0) {
notEmpty.await();
}
Object x = items[takIndex];
if (++takIndex == items.length) takIndex = 0;
count--;
// 唤醒等待"不满"条件的线程
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
精确唤醒的工作原理

notFull.await():当前线程释放锁并进入notFull的等待队列notFull.signal():唤醒notFull队列中的一个线程(通常是等待最久的)notFull.signalAll():唤醒notFull队列中的所有线程
典型应用场景
场景1:生产者-消费者模式
public class ProducerConsumerExample {
private final Lock lock = new ReentrantLock();
private final Condition produced = lock.newCondition();
private final Condition consumed = lock.newCondition();
private int product = 0;
private boolean available = false;
public void produce() throws InterruptedException {
lock.lock();
try {
while (available) {
consumed.await(); // 等待产品被消费
}
product++;
System.out.println("生产者生产,当前产品数:" + product);
available = true;
produced.signal(); // 唤醒等待产品的消费者
} finally {
lock.unlock();
}
}
public void consume() throws InterruptedException {
lock.lock();
try {
while (!available) {
produced.await(); // 等待产品被生产
}
System.out.println("消费者消费,当前产品数:" + product);
available = false;
consumed.signal(); // 唤醒等待的生产者
} finally {
lock.unlock();
}
}
}
优势:生产者只唤醒消费者,消费者只唤醒生产者,避免了无意义的线程唤醒。
场景2:多条件精确控制(打印机队列)
class PrinterManager {
private final Lock lock = new ReentrantLock();
private final Condition colorAvailable = lock.newCondition();
private final Condition blackWhiteAvailable = lock.newCondition();
private int colorPrinters = 3;
private int bwPrinters = 5;
public void useColorPrinter(String task) throws InterruptedException {
lock.lock();
try {
while (colorPrinters == 0) {
colorAvailable.await(); // 等待彩色打印机
}
colorPrinters--;
System.out.println(task + " 使用彩色打印机,剩余:" + colorPrinters);
// 打印任务...
} finally {
lock.unlock();
}
}
public void releaseColorPrinter() {
lock.lock();
try {
colorPrinters++;
colorAvailable.signal(); // 只唤醒等待彩色打印机的线程
} finally {
lock.unlock();
}
}
// 黑白打印机方法类似...
}
高级技巧与注意事项
防止条件丢失
// 错误示例
if (!condition) {
condition.await(); // 可能被虚假唤醒
}
// 正确做法:始终在循环中等待
while (!condition) {
condition.await();
}
使用多个Condition实现优先级
class PriorityQueue<T> {
private final Lock lock = new ReentrantLock();
private final Condition highPriority = lock.newCondition();
private final Condition lowPriority = lock.newCondition();
private Queue<T> highQueue = new LinkedList<>();
private Queue<T> lowQueue = new LinkedList<>();
public void highPriorityPut(T item) {
lock.lock();
try {
highQueue.add(item);
highPriority.signal(); // 优先唤醒高优先级消费者
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while (highQueue.isEmpty() && lowQueue.isEmpty()) {
highPriority.await(); // 等待高优先级
lowPriority.await(); // 等待低优先级
}
if (!highQueue.isEmpty()) {
return highQueue.poll();
}
return lowQueue.poll();
} finally {
lock.unlock();
}
}
}
线程安全地传递条件
class ComplexConditionExample {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
// 使用 volatile 保证可见性
private volatile boolean ready = false;
private volatile String data;
public void producer(String data) {
lock.lock();
try {
this.data = data;
ready = true;
condition.signalAll();
} finally {
lock.unlock();
}
}
public String consumer(long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
lock.lock();
try {
while (!ready) {
if (!condition.await(timeout, unit)) {
throw new TimeoutException("等待超时");
}
}
return data;
} finally {
lock.unlock();
}
}
}
性能对比:Condition vs synchronized
| 特性 | synchronized + wait/notify | Lock + Condition |
|---|---|---|
| 等待队列数 | 1个 | 多个(可精确控制) |
| 唤醒精度 | 无法区分条件 | 可唤醒指定条件 |
| 超时控制 | wait(timeout) | await(timeout, unit) |
| 可中断性 | 支持 | 支持 |
| 公平性 | 不可控 | 可配置公平锁 |
| 检查等待队列 | 不支持 | hasWaiters(Condition) |
选择建议:
- 简单场景(1个条件)→
synchronized更简洁 - 复杂场景(多条件/精确控制)→
Lock + Condition
实战:异步任务协调
class AsyncTaskCoordinator {
private final Lock lock = new ReentrantLock();
private final Condition taskCompleted = lock.newCondition();
private volatile int completedCount = 0;
private final int totalTasks;
public AsyncTaskCoordinator(int totalTasks) {
this.totalTasks = totalTasks;
}
public void waitForAll() throws InterruptedException {
lock.lock();
try {
while (completedCount < totalTasks) {
taskCompleted.await();
}
System.out.println("所有任务完成");
} finally {
lock.unlock();
}
}
public void onTaskCompleted() {
lock.lock();
try {
completedCount++;
taskCompleted.signalAll(); // 唤醒主线程
} finally {
lock.unlock();
}
}
// 测试用例
public static void main(String[] args) throws InterruptedException {
AsyncTaskCoordinator coordinator = new AsyncTaskCoordinator(3);
// 启动3个异步任务
for (int i = 0; i < 3; i++) {
int taskId = i;
new Thread(() -> {
try {
Thread.sleep((long)(Math.random() * 1000));
System.out.println("任务 " + taskId + " 完成");
coordinator.onTaskCompleted();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
coordinator.waitForAll();
System.out.println("主线程继续执行");
}
}
Condition 是实现精确唤醒的黄金标准:
- 隔离等待集:每个条件独立的等待队列
- 精确通知:唤醒特定条件的线程
- 提高效率:减少不必要的上下文切换
- 降低复杂度:避免复杂的条件判断
记住核心原则:把相关的线程组织到同一个Condition上,用不同的Condition隔离不同的等待原因。