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

embedded/2024/12/22 3:49:39/

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

并且能够防止虚假唤醒

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

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

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

java">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;}
}

线程操作实例

java">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.带有超时时间的保护性暂停代码

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

java">  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 类

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

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

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

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

代码实现

java">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对象,保证每个线程 资源对象的唯一性

在主线程中运行为

java"> 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();}}

得到结果如下


http://www.ppmy.cn/embedded/147699.html

相关文章

NV12 、NV21 和 BGR转换

文章目录 前置阅读&#xff1a;YUV格式效果源码 opencv没有提供BGR转NV12或者NV21的方法&#xff0c;这里借助中间过程实现一下。 前置阅读&#xff1a;YUV格式 https://blog.csdn.net/qq_40622955/article/details/144427710 效果 原图 NV12 NV12转BGR NV21 NV21转…

Android 16 关于动态权限使用的变更

权限声明code 在 Android 中&#xff0c;权限的申请分为静态权限和动态权限。 静态权限 静态权限是指在应用的 AndroidManifest.xml 文件中声明的权限。这些权限在应用安装时就会被用户授予。常见的静态权限包括访问互联网、读取用户联系人等。 <manifest xmlns:android&…

地理信息系统(Geographic Information System,GIS)

目录 主要组成部分 主要功能 应用领域 前沿技术与发展趋势 更多学术知识 主要组成部分 数据采集&#xff1a; 通过各种手段&#xff08;如遥感、卫星影像、GPS、地面调研等&#xff09;收集地理和空间数据。这些数据可以是矢量数据&#xff08;点、线、面&#xff09;或栅…

基于时间情境创造与 AI 智能名片 S2B2C 商城小程序源码的零售创新策略研究

摘要&#xff1a;本文聚焦于零售领域的创新发展&#xff0c;深入探讨了时间情境创造在零售中的重要性&#xff0c;并结合 AI 智能名片 S2B2C 商城小程序源码这一新兴技术手段&#xff0c;阐述其如何助力零售企业突破传统模式的局限。通过对国美线上线下融合案例的剖析&#xff…

linux CentOS系统上卸载docker

一、停止Docker服务 首先&#xff0c;需要停止Docker服务。使用systemctl命令来停止Docker服务&#xff1a; bash复制代码sudo systemctl stop docker二、卸载Docker软件包 接下来&#xff0c;使用CentOS的包管理器yum来卸载Docker软件包。根据安装的Docker版本和组件&#…

windows 使用python共享网络给另外一个网卡

# -*- coding: utf-8 -*- import subprocessdef open_share(to_shared_adapter, from_shared_adapter):"""打开以太网的网络共享:return: None"""powershell_script f"""# Register the HNetCfg library (once)# regsvr32 hnetc…

mac编译ijkplayer遇到问题

问题&#xff1a;./init-android.sh git version 2.44.0 pull ffmpeg base : command not founde.sh: line 2: : command not founde.sh: line 5: : command not founde.sh: line 6: tools/pull-repo-base.sh: line 9: syntax error near unexpected token elif ools/pull-re…

3D造型软件solvespace在windows下的编译

3D造型软件solvespace在windows下的编译 在逛开源社区的时候发现了几款开源CAD建模软件&#xff0c;一直囿于没有合适的建模软件&#xff0c;虽然了解了很多的模拟分析软件&#xff0c;却不能使之成为整体的解决方案&#xff0c;从而无法产生价值。opencascad之流虽然可行&…