保护性暂停设计模式是一种在多线程编程中常用的线程同步模式,它允许线程在满足特定条件之前暂停执行,从而避免不必要的资源消耗和竞争条件。
package com.juc.init;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 设计模式-保护性暂停
*/
public class GuardedObjectDemo {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
new Person().start();
}
Thread.sleep(1000);
for (Integer id : MailBoxes.getIds()) {
new Postman(id,"这是一份"+id).start();
}
}
}
// 邮局
class MailBoxes {
private static Map<Integer, GuardedObject> boxes = new Hashtable<>(); // 线程安全的
private static int id = 1;
private static synchronized int genId() { // id原子性自增,对类对象加锁
return id++;
}
public static GuardedObject getGuardedObject(Integer id) {
return boxes.remove(id); // 获取并删除
}
public static GuardedObject createGuardedObject() {
int id = genId();
GuardedObject guardedObject = new GuardedObject(id);
boxes.put(id, guardedObject);
return guardedObject;
}
public static Collection<Integer> getIds() {
return boxes.keySet();
}
}
//收件人
class Person extends Thread {
@Override
public void run() {
GuardedObject guardedObject = MailBoxes.createGuardedObject();
System.out.println("开始收信:id:" + guardedObject.getId());
Object mail = guardedObject.get(); // 阻塞等待
System.out.println("收到信了:" + mail);
}
}
// 寄件人(送件)
class Postman extends Thread {
private Integer id; // 那个柜子
private String mail; // 邮件
public Postman(Integer id, String mail) {
this.id = id;
this.mail = mail;
}
@Override
public void run() {
GuardedObject guardedObject = MailBoxes.getGuardedObject(id);
System.out.println("邮递员把邮件放到:" + id + "号柜子,信件内容:" + mail);
guardedObject.complete(mail);
}
}
// 邮件柜子
class GuardedObject {
private Object response;
private Integer id;
public GuardedObject(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public Object get() {
synchronized (this) {
while (response == null) {
try {
System.out.println("等待数据中……");
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return response;
}
}
public void complete(Object resp) {
synchronized (this) {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
this.response = resp;
this.notifyAll();
}
}
}
通过以上代码设计场景,每一个人(Person)都是一个等待信件的线程,在main方法中创建了三个人去等待信件,同过调用邮局的createGuardedObject指定柜子,隔了一秒后,派三个邮递员(Postman)去送邮局需要送的信件,也就是收件人一开始监听的信件,这个信件放到了GuardedObject(小柜子)中,而邮局中每一个GuardedObject又对于了一个编号,也就是代码中的id。接着分析GuardedObject中的get与complete方法,get方法是用于收件人获取信件内容的途径,通过while(response == null)的循环去等待信件到达,留个问题while改成if可以不可以?,而complete方法是Postman用来放置信件的方法,当放完信件之后调用notifyAll方法,就是向外界广播:“又新的邮件送到啦!”。所以为什么get方法中用的是while循环而不是if,是因为这里唤醒了所有的线程,但是这些线程不一定是真正的收件人,所以要通过while循环直至真的收件人过来取值。
所以最后运行结果是这样的:
收件人指定对应的柜子编号,由Postman通过complete将信件送达 ,而且是一个收件人就对应着一个Postman,也就是这个设计模式是一个一对一的关系。