积跬步至千里

保护性暂停模式(Guarded Suspension Pattern)

简介

保护性暂停模式是一种同步模式,用于解决当线程需要等待某个条件为真才能继续执行的情况。 它本质上是一种条件等待,让线程在条件不满足时进行等待,并在条件满足时被唤醒继续执行。 可以将其理解为一种有条件的等待/通知机制。

核心思想

  • 条件判断: 线程在执行关键操作之前,会先检查一个条件是否满足。
  • 等待: 如果条件不满足,线程会进入等待状态,释放锁,让其他线程有机会改变条件。
  • 通知: 当其他线程改变了条件,使其满足时,会发出通知,唤醒等待的线程。
  • 重新检查: 被唤醒的线程需要重新检查条件,因为可能有多个线程在等待,而条件可能已经被其他线程满足了。

适用场景

  • 生产者-消费者模型: 消费者需要等待生产者生产出数据才能消费。
  • 资源可用性: 线程需要等待资源可用才能使用,例如连接池中的连接。
  • 任务依赖: 线程需要等待某个任务完成才能执行后续操作。
  • 异步结果获取: 线程需要等待异步任务的结果返回。

代码示例

保护性暂停模式经常以一个共享对象的例子引入,这个共享对象是 两个线程同步(通知)的桥梁

@Slf4j
public class TestGuardedModel {

    public static void main(String[] args) {
        GuardedObject guardedObject = new GuardedObject();
        new Thread(() -> {
            try {
                // 等待结果
                log.debug("等待结果");
                Object o = guardedObject.get();
                log.debug("结果:{}", JSON.toJSONString(o));
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "t1").start();


        new Thread(() -> {
            log.debug("执行模拟下载");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            log.debug("放入结果");
            guardedObject.complete("download success");
        }, "t2").start();
    }
}


class GuardedObject {
    // 结果
    private Object response;

    // 获取结果
    public Object get() throws InterruptedException {
        synchronized (this) {
            while (response == null) {
                //  释放锁,将 Owner 设置为 null。并添加到 Monitor 的 Wait Set (等待池) 中
                this.wait();
            }
            return this.response;
        }
    }

    // 产生结果
    public void complete(Object res) {
        synchronized (this) {
            this.response = res;
            // 唤醒 Monitor 中 Wait Set (等待池)中的线程,移到 Entry Set(锁池),从而重新竞争锁
            this.notifyAll();
        }
    }
}

可能会想到,上面第一个例子中的 t2 线程如果是耗时操作,那么 t1 线程会一直死等,所以可以优化一下,加入超时时间

@Slf4j
public class TestGuardedModel2 {
    public static void main(String[] args) {
        GuardedObject2 guardedObject = new GuardedObject2();
        new Thread(() -> {
            try {
                // 等待结果
                log.debug("等待结果");
                Object o = guardedObject.get(3100);
                log.debug("结果:{}", JSON.toJSONString(o));
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "t1").start();


        new Thread(() -> {
            log.debug("执行模拟下载");
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.debug("放入结果");
            guardedObject.complete("download success");

        }, "t2").start();
    }
}


class GuardedObject2 {

    // 结果
    private Object response;

    // 获取结果
    public Object get(long timeout) throws InterruptedException {
        synchronized (this) {
            // 开始时间
            long begin = System.currentTimeMillis();
            long passedTime = 0;
            while (response == null && timeout >= passedTime) {
                // 线程会释放当前对象的监视器锁(synchronized锁),
                // 线程进入该对象的等待集(waitset)
                // 如果在指定的超时时间内没有被唤醒,线程会自动从waitset转移到入口集(entry set),等待重新获取对象的监视器锁
                // 当线程成功获取锁后,会从 wait() 调用后的下一行代码继续执行,也就是从  passedTime = System.currentTimeMillis() - begin; 执行
                this.wait(timeout - passedTime);
                passedTime = System.currentTimeMillis() - begin;
            }

            return this.response;
        }
    }


    // 产生结果
    public void complete(Object res) {
        synchronized (this) {
            this.response = res;
            this.notifyAll();
        }
    }
}

接下来,举一个生产者-消费者模型的例子

public class GuardedSuspensionTest {

    private final Queue<Integer> queue = new LinkedList<>();
    private final int maxSize = 10;
    private final Object lock = new Object(); // 锁对象

    // 生产者
    public void produce() throws InterruptedException {
        Random random = new Random();
        while (true) {
            synchronized (lock) {
                // 条件判断:队列是否已满
                while (queue.size() == maxSize) {
                    System.out.println("队列已满,生产者等待...");
                    lock.wait(); // 队列满,生产者进入等待状态,释放锁
                }

                int data = random.nextInt(100);
                queue.offer(data);
                System.out.println("生产者生产数据: " + data + ", 队列大小: " + queue.size());

                lock.notifyAll(); // 唤醒等待的消费者
                Thread.sleep(random.nextInt(500)); // 模拟生产时间
            }
        }
    }

    // 消费者
    public void consume() throws InterruptedException {
        Random random = new Random();
        while (true) {
            synchronized (lock) {
                // 条件判断:队列是否为空
                while (queue.isEmpty()) {
                    System.out.println("队列为空,消费者等待...");
                    lock.wait(); // 队列空,消费者进入等待状态,释放锁
                }

                Integer data = queue.poll();
                System.out.println("消费者消费数据: " + data + ", 队列大小: " + queue.size());

                lock.notifyAll(); // 唤醒等待的生产者
                Thread.sleep(random.nextInt(500)); // 模拟消费时间
            }
        }
    }

    public static void main(String[] args) {
        GuardedSuspensionTest guardedSuspension = new GuardedSuspensionTest();

        // 创建生产者线程
        Thread producerThread = new Thread(() -> {
            try {
                guardedSuspension.produce();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 重新设置中断标志
                System.err.println("生产者线程被中断: " + e.getMessage());
            }
        }, "ProducerThread");

        // 创建消费者线程
        Thread consumerThread = new Thread(() -> {
            try {
                guardedSuspension.consume();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 重新设置中断标志
                System.err.println("消费者线程被中断: " + e.getMessage());
            }
        }, "ConsumerThread");

        producerThread.start();
        consumerThread.start();

        // 主线程休眠一段时间,然后中断生产者和消费者线程
        try {
            Thread.sleep(5000); // 运行5秒
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("主线程被中断: " + e.getMessage());
        }

        System.out.println("主线程中断生产者和消费者线程...");
        producerThread.interrupt();
        consumerThread.interrupt();
    }
}
posted @ 2025-03-11 16:25  大阿张  阅读(65)  评论(0)    收藏  举报