保护性暂停模式(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();
}
}

浙公网安备 33010602011771号