如何用 signalAll() 实现“广播”式唤醒系统
                           
天天向上
发布: 2025-07-20 11:14:48

原创
854 人浏览过

你想用 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. 参考资料


更多详细内容请关注其他相关文章!

发表回复 0

Your email address will not be published. Required fields are marked *