保护性暂停模式

2.模式之保护性暂停

QQ截图20220222162018

public class Test2 {
	public static void main(String[] args) {
		String hello = "hello thread!";
		Guarded guarded = new Guarded();
		new Thread(()->{
			System.out.println("想要得到结果");
			synchronized (guarded) {
				System.out.println("结果是:"+guarded.getResponse());
			}
			System.out.println("得到结果");
		}).start();

		new Thread(()->{
			System.out.println("设置结果");
			synchronized (guarded) {
				guarded.setResponse(hello);
			}
		}).start();
	}
}

class Guarded {
	/**
	 * 要返回的结果
	 */
	private Object response;
	
    //优雅地使用wait/notify
	public Object getResponse() {
		//如果返回结果为空就一直等待,避免虚假唤醒
		while(response == null) {
			synchronized (this) {
				try {
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		return response;
	}

	public void setResponse(Object response) {
		this.response = response;
		synchronized (this) {
			//唤醒休眠的线程
			this.notifyAll();
		}
	}

	@Override
	public String toString() {
		return "Guarded{" +
				"response=" + response +
				'}';
	}
}

但是,这么写有一个弊端,接收文件的线程如果得不到文件就会一直等待,我们要让它等待一段时间自己停下

public Object getResponse(long time) {
		synchronized (this) {
			//获取开始时间
			long currentTime = System.currentTimeMillis();
			//用于保存已经等待了的时间
			long passedTime = 0;
			while(response == null) {
				//看经过的时间-开始时间是否超过了指定时间
				long waitTime = time -passedTime;
				if(waitTime <= 0) {
					break;
				}
				try {
                   	//等待剩余时间
					this.wait(waitTime);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//获取当前时间
				passedTime = System.currentTimeMillis()-currentTime		
            }
		}
		return response;
	}

这样,就解决了我们的问题。

其实,这种思路是借鉴了join底层的思路。

join的原理解析
//点开带参数的方法
public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            //下面的与上面我们改进的保护暂停类似
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

3.保护性暂停模式的多任务解耦版(添加了一个管理类)

QQ截图20220223173539

图中 Futures 就好比居民楼一层的信箱(每个信箱有房间编号),左侧的 t0,t2,t4 就好比等待邮件的居民,右侧的 t1,t3,t5 就好比邮递员

如果需要在多个类之间使用 GuardedObject 对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类,这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理。

  • 任务类
class GuardedObject {
 // 标识 Guarded Object
 private int id;
 
 public GuardedObject(int id) {this.id = id; }
 
public int getId() { return id;}
 
 // 结果
 private Object response;
 
  // 获取结果
 // timeout 表示要等待多久 2000
 public Object get(long timeout) {
 
  synchronized (this) {

 // 开始时间 
 long begin = System.currentTimeMillis();
 // 经历的时间
 long passedTime = 0;
      
 while (response == null) {
 
 // 这一轮循环应该等待的时间
 long waitTime = timeout - passedTime;
 // 经历的时间超过了最大等待时间时,退出循环
 if (timeout - passedTime <= 0) {break;}
 
 try {
 
     this.wait(waitTime); 
 
 } catch (InterruptedException e) {
 
     e.printStackTrace();
  }
 
 // 求得经历时间
 
     passedTime = System.currentTimeMillis() - begin; 
 }

      return response;
 }
}

 // 产生结果
 public void complete(Object response) {
 
 synchronized (this) {
 // 给结果成员变量赋值
 
     this.response = response;
 
     this.notifyAll();
 }
}
}
  • 任务的管理类–解耦任务

每个任务对应一个id

class Mailboxes {
 private static Map<Integer, GuardedObject> boxes = new Hashtable<>();
 private static int id = 1;
 // 产生唯一 id
  private static synchronized int generateId() {
 
      return id++;
 }
 //提交任务
 public static GuardedObject getGuardedObject(int id) {
 
     return boxes.remove(id);
 }
 //创建任务
 public static GuardedObject createGuardedObject() {
 
     GuardedObject go = new GuardedObject(generateId());
 
     boxes.put(go.getId(), go);

     return go;
 }
 
 public static Set<Integer> getIds() {

     return boxes.keySet();
 }
}
  • 获得任务结果的类
class People extends Thread{
 @Override
 public void run() {
    // 收信
     GuardedObject guardedObject = Mailboxes.createGuardedObject();
 
     log.debug("开始收信 id:{}", guardedObject.getId());
 
     Object mail = guardedObject.get(5000);
 
     log.debug("收到信 id:{}, 内容:{}", guardedObject.getId(), mail);
 }
}
  • 执行任务的类
class Postman extends Thread {
 
    private int id;
 
    private String mail;
 
 public Postman(int id, String mail) {
 
     this.id = id;
 
     this.mail = mail;
 }
 
 @Override
 public void run() {
 
     GuardedObject guardedObject = Mailboxes.getGuardedObject(id);
 
     log.debug("送信 id:{}, 内容:{}", id, mail);
 
     guardedObject.complete(mail);
 }
}
  • 测试类
public static void main(String[] args) throws InterruptedException {

 for (int i = 0; i < 3; i++) {
 
     new People().start();
 }
 
 Sleeper.sleep(1);
 
 for (Integer id : Mailboxes.getIds()) {
 
     new Postman(id, "内容" + id).start();
 }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值