欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > 多线程设计模式-保护性暂停之面向对象

多线程设计模式-保护性暂停之面向对象

2025/10/14 19:20:56 来源:https://blog.csdn.net/2202_75352238/article/details/144575414  浏览:    关键词:多线程设计模式-保护性暂停之面向对象

保护性暂停模式,也是多线程的一种固定模式,使用着这种模式时候,当线程在访问某个对象时,发现条件不满足时,就暂时挂起等待条件满足时再次访问。

并且能够防止虚假唤醒

那我们不禁要思考,在多线程终止模式的时候,用interrupt或者一个共享变量作为标记位,来进行优雅的终止,

在 保护性暂停的时候呢,也是 传入一个由线程定义的时间变量,发现条件不满足,进入 等待,即使其中被虚假唤醒,只要条件不满足,我们仍进入挂起状态,直到设置的等待时间结束

1.一直等待条件满足的 情况 代码

public class GuardedSuspension{private  Integer id;public GuardedSuspension(Integer id){this.id=id;}public GuardedSuspension(){}public Integer getId(){return this.id;}private  Object guardedSuspensionObject;public synchronized void setObject(Object object) throws InterruptedException {this.guardedSuspensionObject=object;notifyAll();}public synchronized Object  getObject() throws InterruptedException {while (this.guardedSuspensionObject==null){System.out.println("调用了wait");wait();}return guardedSuspensionObject;}
}

线程操作实例

package thread;
//   同步模式  保护性暂停
public class ThreadTest4GuardedSuspension {public static void main(String[] args) {long start = System.currentTimeMillis();GuardedSuspension object = new GuardedSuspension();new Thread(new Runnable() {@Overridepublic void run() {try {Object object1 = object.getObject();if(object1==null){System.out.println("没有东西");}else{System.out.println("获得了");}} catch (InterruptedException e) {throw new RuntimeException(e);}}},"t1").start();try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}new Thread(new Runnable() {@Overridepublic void run() {try {object.setObject(new Object());System.out.println("放置了");long end= System.currentTimeMillis();System.out.println("线程t1经过的时间为"+(start-end));} catch (InterruptedException e) {throw new RuntimeException(e);}}},"t2").start();}
}

当我们t1线程 一直获取不到 object对象的时候,t1会一直等待,直到一秒后 t2运行在同一个类中放置,然后t1 被唤醒,再次运行

2.带有超时时间的保护性暂停代码

其实就是 在原有的基础上加了一个重载的方法

  public synchronized Object  getObject(Long time) throws InterruptedException {//超时等待 必须要让 该线程 等够 一定的时间long start = System.currentTimeMillis();//开始时间long passTime=0;// 4 slong waitTime=time-passTime;while (guardedSuspensionObject==null||waitTime>0){waitTime=time-passTime;//上面三行 代码 都可以根据自身使用情况 来自由 变换逻辑 ,如果以//超时时间为主,那么可以不用改变,如果以 是否 暂停  停止等待的条件为主//可以改成如下//            while (guardedSuspensionObject==null){//                long  waitTime=time-passTime;if(waitTime<=0){System.out.println("等待时间到了");break;}wait(waitTime);//2s 被唤醒了passTime=System.currentTimeMillis()-start;}return guardedSuspensionObject;}

,当我们被唤醒的时候, 即使  线程满足  停止等待的条件,但是等待时间没到 他还是要休眠 , 或者 可以把while 改成,只要 线程满足 停止等待的条件 ,无论等待时间是否到没到,都停止等待,这个主要 在于 使用者 更希望是     等待时间的优先级更高还是  暂停等待的优先级更高来设置

而精华部分 ,就是每次被唤醒的时候,对于 重置等待时间的处理

面向对象思维

我们 上面对于超时等待的处理 是 一个线程 等待 另外一个 线程的 资源放置

那么 如果是 假如有3 个线程等待  另外三个线程放置 object , 这个时候就会 变成 三个线程中的 一对一

那么 我们先不看代码,而是想想这个代码要怎么写

思考

写的话就会刚开始想到,我for 三个线程出来,在for 三个发放置资源的线程,在for三个 中间的

GuardedSuspension 类

这样的话虽然可以实现,但是 整体的拓展性,代码的健壮性是不够友好的。

这个时候可以用面向对象的思维

我们把 等待资源的线程抽成一个对象,放置资源的线程抽成一个对象

用一个中间的类 ,来联系 这两个对象,然后 让 主线程 去创建对象去操作,这样代码的扩展性能也是更优解

代码实现

class Postman extends Thread {private int id;private String message;//信件内容public Postman(int id, String mail) {this.id = id;this.message = mail;}@Overridepublic void run() {GuardedSuspension guardedSuspension = MailBox.getGuardedSuspension(id);System.out.println("开始送信了内容为"+message);try {guardedSuspension.setObject(message);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
class MailBox {private static final ConcurrentHashMap<Integer, GuardedSuspension> concurrentHashMap= new ConcurrentHashMap<>();private static Integer id = 1;private static synchronized int generateId() {return id++;}public static GuardedSuspension createGuardedSuspension() {GuardedSuspension guardedSuspension = new GuardedSuspension(generateId());concurrentHashMap.put(guardedSuspension.getId(), guardedSuspension);return guardedSuspension;}public static GuardedSuspension getGuardedSuspension(Integer id) {return concurrentHashMap.remove(id);}public static Set<Integer> getIds(){ConcurrentHashMap.KeySetView<Integer, GuardedSuspension> integers = concurrentHashMap.keySet();return integers;}

上面代码中,我们用concurrenthashmap来保证线程安全,用一个单例的 map对象,保证每个线程 资源对象的唯一性

在主线程中运行为

 public static void main(String[] args) throws InterruptedException {for (int i = 0; i <3 ; i++) {People people = new People();people.start();}Thread.sleep(1000);for (Integer id : MailBox.getIds()) {new Postman(id,"发送者id是"+id).start();}}

得到结果如下

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词