如何用 signalAll() 实现“广播”式唤醒系统
你想用 Condition.signalAll() 实现“广播”式唤醒系统,核心就是唤醒所有等待某个条件的线程,让它们一起响应状态变化或事件通知。以下给你一份详细实战示例与设计思路,帮助你快速掌握这个模式。
1. 设计目标
- 有多个线程等待某个条件(如共享状态变化)
- 主线程或某个线程状态变化时,一次调用
signalAll()唤醒所有等待线程 - 唤醒的线程都要重新检查条件,确保安全执行
2. 基本实现代码示例(Java)
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class BroadcastSystem {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
// 共享状态
private boolean conditionMet = false;
// 等待线程执行方法
public void awaitCondition() throws InterruptedException {
lock.lock();
try {
while (!conditionMet) {
System.out.println(Thread.currentThread().getName() + " 等待中...");
condition.await();
}
System.out.println(Thread.currentThread().getName() + " 被唤醒,条件满足!");
// 处理后,重置状态或根据实际业务决定是否重置
} finally {
lock.unlock();
}
}
// 广播唤醒所有等待线程
public void broadcast() {
lock.lock();
try {
conditionMet = true; // 改变共享状态
condition.signalAll(); // 广播唤醒所有等待线程
System.out.println("广播信号,所有等待线程被唤醒");
} finally {
lock.unlock();
}
}
// 重置状态以便重新等待
public void reset() {
lock.lock();
try {
conditionMet = false;
System.out.println("条件重置,等待线程可再次阻塞");
} finally {
lock.unlock();
}
}
// 测试
public static void main(String[] args) throws InterruptedException {
BroadcastSystem bs = new BroadcastSystem();
// 启动多个等待线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
bs.awaitCondition();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "线程-" + i).start();
}
Thread.sleep(2000);
// 主线程广播唤醒
bs.broadcast();
Thread.sleep(2000);
// 重置状态,模拟再次等待
bs.reset();
// 再次启动等待线程
for (int i = 5; i < 7; i++) {
new Thread(() -> {
try {
bs.awaitCondition();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "线程-" + i).start();
}
Thread.sleep(2000);
bs.broadcast();
}
}
3. 关键点说明
| 关键点 | 说明 |
|---|---|
使用 while 循环 | 防止虚假唤醒,唤醒后重新检测条件 |
共享状态 conditionMet | 作为条件判断的依据,保证线程唤醒后执行安全 |
signalAll() 广播 | 一次唤醒所有等待线程,保证所有线程均能获得通知 |
| 锁保护状态变量 | 共享变量修改必须在锁内,保证可见性和原子性 |
| 线程重用逻辑 | 状态重置方便多轮等待与广播,适合循环广播场景 |
4. 典型适用场景
- 多个消费者等待资源可用,资源变化时通知所有消费者重新竞争
- 配置、状态变更广播给多个监听线程
- 线程屏障、事件通知,所有等待线程同时继续执行
5. 注意事项
signalAll()会唤醒所有线程,容易引起“惊群效应”,短时间内大量线程争抢 CPU,可能导致性能下降- 在高并发场景下,可以结合线程池限流、分批唤醒等策略优化性能
- 确保唤醒线程都能安全判断条件,避免错误执行
6. 延伸:分组广播与信号控制
你可以用多个 Condition 对象区分不同“广播组”,实现更细粒度的唤醒。
7. 参考资料
- Java官方文档 – Condition接口
- 《Java并发编程实战》— 线程间通信章节
- ReentrantLock与Condition源码分析
更多详细内容请关注其他相关文章!