Java 中 Condition 对象如何配合线程池管理任务调度
Condition 不是线程池调度器,而是与 ReentrantLock 配合实现任务内精细等待/唤醒的协作工具;它不参与线程池的任务分发,仅用于任务执行过程中基于条件的挂起与唤醒,需配合锁使用且不影响其他任务调度。 说实话,很多开发者刚接触 Condition 时,容易把它和线程池的调度机制混为一
Condition 不是线程池调度器,而是与 ReentrantLock 配合实现任务内精细等待/唤醒的协作工具;它不参与线程池的任务分发,仅用于任务执行过程中基于条件的挂起与唤醒,需配合锁使用且不影响其他任务调度。

说实话,很多开发者刚接触 Condition 时,容易把它和线程池的调度机制混为一谈。其实 Condition 是 java.util.concurrent.locks 包的一员,专门与 ReentrantLock 搭档,干的是“精细等待/唤醒”的细活。而线程池(比如 ThreadPoolExecutor)的任务调度,靠的是内部队列、工作线程和拒绝策略那套体系。两者分工清清楚楚,但在特定场景下——比如线程池任务里需要自定义阻塞等待逻辑——它们可以配合使用。
Condition 不是线程池调度器,而是任务内部的协作工具
线程池的职责是“把任务分发给空闲线程执行”,而 Condition 解决的是“某个任务执行过程中,如何让当前线程暂停并等待某个条件成立”。举个例子:一个提交到线程池的任务,需要等待外部信号(比如资源就绪、数据到达)才能继续执行。这时在任务代码里用 Lock + Condition 实现挂起与唤醒,远比让线程池反复轮询或直接阻塞整个工作线程要优雅。
await()会让当前线程释放锁并进入等待状态,不占用 CPU,也不影响线程池中其他任务执行。signal()或signalAll()由其他线程(可能是另一个任务、主线程或定时器)调用,唤醒等待中的任务线程。- 必须持有同一把
ReentrantLock才能使用,不能搭配synchronized或线程池默认锁。
典型配合场景:带条件依赖的异步任务
假设你有一个线程池,提交了两类任务:一类生产数据(ProducerTask),一类消费数据(ConsumerTask)。ConsumerTask 不能立即执行,得等 ProducerTask 写入共享缓冲区后才开始处理。这时可以在共享资源上定义 Lock 和 Condition:
class SharedBuffer {
private final ReentrantLock lock = new ReentrantLock();
private final Condition dataReady = lock.newCondition();
private volatile boolean hasData = false;
void putData() {
lock.lock();
try {
// 模拟写入
hasData = true;
dataReady.signal(); // 唤醒等待的消费者任务
} finally {
lock.unlock();
}
}
void waitForData() throws InterruptedException {
lock.lock();
try {
while (!hasData) {
dataReady.await(); // 当前任务线程在此挂起,释放锁,不阻塞线程池
}
} finally {
lock.unlock();
}
}
}
ConsumerTask 在 run() 里调用 waitForData(),挂起自身;ProducerTask 完成后调用 putData() 唤醒它。线程池照常调度其他任务,只有这个 ConsumerTask 所在的线程暂时让出执行权。
注意与线程池配置的协调
用 Condition 时,要小心别跟线程池的行为“打架”:
- 不要在
await()前后长时间持有锁,不然并发性会大打折扣。 - 避免在核心线程数极少的线程池(比如
newFixedThreadPool(1))里大量使用 await,否则容易造成任务堆积和响应延迟。 - 如果任务可能长期 await(比如等待外部事件),最好设置超时(
awaitNanos(timeout)),配合重试或取消逻辑,防止任务永久挂起。 - Condition 唤醒不保证立即执行——被唤醒的任务得重新竞争锁,而且还得排队等线程池分配执行机会。
替代方案对比:何时用 Condition,何时用其他机制
对于任务调度级的协调,优先考虑更高层的并发工具会更省心:
- 需要等待某个异步结果?用
CompletableFuture或Future链式编排。 - 需要定时/周期性调度?用
ScheduledThreadPoolExecutor。 - 需要跨任务传递信号且逻辑复杂?用
BlockingQueue(比如LinkedBlockingQueue)配合take()/put(),底层已经封装了类似 Condition 的等待机制。 - 只有当标准队列或 Future 无法满足细粒度条件判断(比如“等待缓冲区中某类数据达到阈值”)时,才需要在任务内部手动引入 Lock + Condition。


































