当前位置: 首页 > news >正文

Java并发(二十三)----同步模式之保护性暂停

1、定义

即 Guarded Suspension,用在一个线程等待另一个线程的执行结果

要点

  • 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject

  • 如果有结果不断从一个线程到另一个线程那么可以使用消息队列

  • JDK 中,join 的实现、Future 的实现,采用的就是此模式

  • 因为要等待另一方的结果,因此归类到同步模式

2、实现

class GuardedObject {
​// 结果private Object response;private final Object lock = new Object();
​// 获取结果public Object get() {synchronized (lock) {// 条件不满足则等待while (response == null) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}return response;}}
​// 产生结果public void complete(Object response) {synchronized (lock) {// 条件满足,通知等待线程this.response = response;lock.notifyAll();}}
}

3、应用

一个线程等待另一个线程的执行结果

public static void main(String[] args) {GuardedObject guardedObject = new GuardedObject();new Thread(() -> {try {// 子线程执行下载List<String> response = download(); // 模拟下载操作log.debug("download complete...");guardedObject.complete(response);} catch (IOException e) {e.printStackTrace();}}).start();
​log.debug("waiting...");// 主线程阻塞等待Object response = guardedObject.get();log.debug("get response: [{}] lines", ((List<String>) response).size());
​
}

执行结果

08:42:18.568 [main] c.TestGuardedObject - waiting...
08:42:23.312 [Thread-0] c.TestGuardedObject - download complete...
08:42:23.312 [main] c.TestGuardedObject - get response: [3] lines

4、带超时版 GuardedObject

如果要控制超时时间呢

class GuardedObjectV2 {
​private Object response;private final Object lock = new Object();
​public Object get(long millis) {synchronized (lock) {// 1) 记录最初时间long begin = System.currentTimeMillis();// 2) 已经经历的时间long timePassed = 0;while (response == null) {// 4) 假设 millis 是 1000,结果在 400 时唤醒了,那么还有 600 要等long waitTime = millis - timePassed;log.debug("waitTime: {}", waitTime);if (waitTime <= 0) {log.debug("break...");break;}try {lock.wait(waitTime);  // 注意这里并不是 mills,防止虚假唤醒} catch (InterruptedException e) {e.printStackTrace();}// 3) 如果提前被唤醒,这时已经经历的时间假设为 400timePassed = System.currentTimeMillis() - begin;log.debug("timePassed: {}, object is null {}", timePassed, response == null);}return response;}}
​public void complete(Object response) {synchronized (lock) {// 条件满足,通知等待线程this.response = response;log.debug("notify...");lock.notifyAll();}}
}

测试,没有超时

public static void main(String[] args) {GuardedObjectV2 v2 = new GuardedObjectV2();new Thread(() -> {sleep(1); // 睡眠1秒v2.complete(null);sleep(1);v2.complete(Arrays.asList("a", "b", "c"));}).start();
​Object response = v2.get(2500);if (response != null) {log.debug("get response: [{}] lines", ((List<String>) response).size());} else {log.debug("can't get response");}
}

输出

08:49:39.917 [main] c.GuardedObjectV2 - waitTime: 2500
08:49:40.917 [Thread-0] c.GuardedObjectV2 - notify...
08:49:40.917 [main] c.GuardedObjectV2 - timePassed: 1003, object is null true
08:49:40.917 [main] c.GuardedObjectV2 - waitTime: 1497
08:49:41.918 [Thread-0] c.GuardedObjectV2 - notify...
08:49:41.918 [main] c.GuardedObjectV2 - timePassed: 2004, object is null false
08:49:41.918 [main] c.TestGuardedObjectV2 - get response: [3] lines

测试,超时

// 等待时间不足
List<String> lines = v2.get(1500);

输出

08:47:54.963 [main] c.GuardedObjectV2 - waitTime: 1500
08:47:55.963 [Thread-0] c.GuardedObjectV2 - notify...
08:47:55.963 [main] c.GuardedObjectV2 - timePassed: 1002, object is null true
08:47:55.963 [main] c.GuardedObjectV2 - waitTime: 498
08:47:56.461 [main] c.GuardedObjectV2 - timePassed: 1500, object is null true
08:47:56.461 [main] c.GuardedObjectV2 - waitTime: 0
08:47:56.461 [main] c.GuardedObjectV2 - break...
08:47:56.461 [main] c.TestGuardedObjectV2 - can't get response
08:47:56.963 [Thread-0] c.GuardedObjectV2 - notify...

5、多任务版 GuardedObject

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

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

新增 id 用来标识 Guarded Object

class GuardedObject {
​// 标识 Guarded Objectprivate int id;
​public GuardedObject(int id) {this.id = id;}
​public int getId() {return id;}
​// 结果private Object response;
​// 获取结果// timeout 表示要等待多久 2000public Object get(long timeout) {synchronized (this) {// 开始时间 15:00:00long begin = System.currentTimeMillis();// 经历的时间long passedTime = 0;while (response == null) {// 这一轮循环应该等待的时间long waitTime = timeout - passedTime;// 经历的时间超过了最大等待时间时,退出循环if (timeout - passedTime <= 0) {break;}try {this.wait(waitTime); // 虚假唤醒 15:00:01} catch (InterruptedException e) {e.printStackTrace();}// 求得经历时间passedTime = System.currentTimeMillis() - begin; // 15:00:02  1s}return response;}}
​// 产生结果public void complete(Object response) {synchronized (this) {// 给结果成员变量赋值this.response = response;this.notifyAll();}}
}

中间解耦类

class Mailboxes {private static Map<Integer, GuardedObject> boxes = new Hashtable<>();
​private static int id = 1;// 产生唯一 idprivate static synchronized int generateId() {return id++;}
​public static GuardedObject getGuardedObject(int id) {return boxes.remove(id);  // 注意这里的remove,防止堆溢出}
​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{@Overridepublic 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;}
​@Overridepublic 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);// 睡眠1秒for (Integer id : Mailboxes.getIds()) {new Postman(id, "内容" + id).start();}
}

某次运行结果

10:35:05.689 c.People [Thread-1] - 开始收信 id:3
10:35:05.689 c.People [Thread-2] - 开始收信 id:1
10:35:05.689 c.People [Thread-0] - 开始收信 id:2
10:35:06.688 c.Postman [Thread-4] - 送信 id:2, 内容:内容2
10:35:06.688 c.Postman [Thread-5] - 送信 id:1, 内容:内容1
10:35:06.688 c.People [Thread-0] - 收到信 id:2, 内容:内容2
10:35:06.688 c.People [Thread-2] - 收到信 id:1, 内容:内容1
10:35:06.688 c.Postman [Thread-3] - 送信 id:3, 内容:内容3
10:35:06.689 c.People [Thread-1] - 收到信 id:3, 内容:内容3

相关文章:

Java并发(二十三)----同步模式之保护性暂停

1、定义 即 Guarded Suspension&#xff0c;用在一个线程等待另一个线程的执行结果 要点 有一个结果需要从一个线程传递到另一个线程&#xff0c;让他们关联同一个 GuardedObject 如果有结果不断从一个线程到另一个线程那么可以使用消息队列 JDK 中&#xff0c;join 的实现…...

###C语言程序设计-----C语言学习(9)#函数基础

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 一. 基础知识的学习 1.函数的定义 函数是一个完成特定工作的独立程序模块&…...

Dockerfile文件参数配置和使用

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…...

Java实现婚恋交友网站 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 会员管理模块2.3 新闻管理模块2.4 相亲大会管理模块2.5 留言管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 会员信息表3.2.2 新闻表3.2.3 相亲大会表3.2.4 留言表 四、系统展示五、核心代码5.…...

React16源码: React中详解在渲染阶段Suspend的源码实现

Suspend 挂起详解 1 &#xff09;概述 在react的更新过程当中&#xff0c;它的任务是可以被挂起的&#xff0c;也就是 Suspend关于 Suspend 字面意思就是挂起在某次更新的任务更新完成之后&#xff0c;暂时不提交 在 react更新中&#xff0c;分为两个阶段&#xff0c;首先是re…...

mac电脑风扇控制软件:Macs Fan Control Pro for mac 激活版

Macs Fan Control 是一款专门为 Mac 用户设计的软件&#xff0c;它可以帮助用户控制和监控 Mac 设备的风扇速度和温度。这款软件允许用户手动调整风扇速度&#xff0c;以提高设备的散热效果&#xff0c;减少过热造成的风险。 Macs Fan Control 可以在菜单栏上显示当前系统温度和…...

easyexcel解析跨多行的数据

在使用easyexcel解析excel文件的时候&#xff0c;存在某列横跨多行&#xff0c;那么存在解析出的对象的某些属性是没有值的&#xff0c;那么我们要怎么处理呢&#xff1f;代码如下 定义实体对应excel文件 public class EtcParkingReconciliationDailyImportModel implements S…...

双目相机立体匹配基础

双目匹配就是用左相机和右相机去拍摄同一个点&#xff0c;目的是找到三维世界的同一个点&#xff0c;也就是在左相机和右相机中的成像点之间的像素差&#xff08;视差&#xff09;&#xff0c;根据视差去求解深度&#xff0c;那么找到左相机点到右相机的同一个对应点这个过程就…...

【图论】网络流

网络流目前只整理模板&#xff0c;学习的话这篇博客可能不太适合 代码参考下方博客&#xff0c;加了一些自己的注释 算法学习笔记(28): 网络流究级的最大流算法&#xff1a;ISAP与HLPP FF 和 EK 仅用作理解代码&#xff0c;赛时请使用 Dinic 或 ISAP 下文建图方式都基于链式…...

【Matplotlib】figure方法 你真的会了吗!?

&#x1f388;个人主页&#xff1a;甜美的江 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;matplotlib &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…...

[C++]继承(续)

一、基类和派生类对象赋值转换 在public继承时&#xff0c;父类和子类是一个“is - a”的关系。 子类对象赋值给父类对象/父类指针/父类引用&#xff0c;我们认为是天然的&#xff0c;中间不产生临时对象&#xff0c;也叫作父子类赋值兼容规则&#xff08;切割/切片&#xff…...

恒创科技:服务器内存不足影响大吗?

​  服务器在为网站、应用程序和在线服务提供支持方面发挥着关键作用。这些服务器需要提供最佳性能&#xff0c;以确保正常无缝的用户体验&#xff0c;而RAM是显著影响服务器性能的关键配置之一。 RAM 是一种随机存取存储器&#xff0c;计算机和服务器使用它来临时存储正在使…...

深入理解网络通信和TCP/IP协议

目录 计算机网络是什么&#xff1f; 定义和分类 计算机网络发展简史 计算机网络体系结构 OSI 七层模型 TCP/IP 模型 TCP/IP 协议族 TCP/IP 网络传输中的数据 地址和端口号 MAC地址 IP 地址 端口号 为什么端口号有65535个&#xff1f; 综述 TCP 特性 TCP 三次握…...

Open CASCADE学习|分割曲线

1、通过参数进行分割 分别获得曲线的 FirstParameter 和 LastParameter &#xff0c;然后对参数进行分割&#xff0c;获得n个ui&#xff0c;并对每个ui调用D0&#xff08;获得这个点的坐标值&#xff09;或D1&#xff08;获得这个点的坐标值和切向量&#xff09;。这个方法的优…...

vulhub中Adminer远程文件读取漏洞复现(CVE-2021-43008)

Adminer是一个PHP编写的开源数据库管理工具&#xff0c;支持MySQL、MariaDB、PostgreSQL、SQLite、MS SQL、Oracle、Elasticsearch、MongoDB等数据库。 在其版本1.12.0到4.6.2之间存在一处因为MySQL LOAD DATA LOCAL导致的文件读取漏洞。 参考链接&#xff1a; https://gith…...

MOS管驱动电流估算-Qg参数

MOS管驱动电流估算 例&#xff1a;FDH45N50F如下参数&#xff1a; 有人可能会这样计算&#xff1a; 开通电流 带入数据得 关断电流 带入数据得 于是乎得出这样的结论&#xff0c;驱动电流只需 250mA左右即可。仔细想想这样计算对吗&#xff1f; 这里必须要注意这样一个条件细…...

Vision Transfomer系列第一节---从0到1的源码实现

本专栏主要是深度学习/自动驾驶相关的源码实现,获取全套代码请参考 这里写目录标题 准备逐步源码实现数据集读取VIt模型搭建hand类别和位置编码类别编码位置编码 blocksheadVIT整体 Runner(参考mmlab)可视化 总结 准备 本博客完成Vision Transfomer(VIT)模型的搭建和flowers数…...

【CSS + ElementUI】更改 el-carousel 指示器样式且隐藏左右箭头

需求 前三条数据以走马灯形式展现&#xff0c;指示器 hover 时可以切换到对应内容 实现 <template><div v-loading"latestLoading"><div class"upload-first" v-show"latestThreeList.length > 0"><el-carousel ind…...

Ubuntu 22.04 上安装和使用 Go

1.下载  All releases - The Go Programming Language //https://golang.google.cn/dl/wget https://golang.google.cn/dl/go1.21.6.linux-amd64.tar.gz 2.在下载目录下执行&#xff0c;现在&#xff0c;使用以下命令将文件提取到 “/usr/local ” 位置 sudo tar -C /usr/…...

ES6-const

一、基本用法 - 语法&#xff1a;const 标识符初始值;注意:const一旦声明变量&#xff0c;就必须立即初始化&#xff0c;不能留到以后赋值 - 规则&#xff1a;1.const 声明一个只读的常量&#xff0c;一旦声明&#xff0c;常量的值就不能改变2.const 其实保证的不是变量的值不…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用

前言&#xff1a;我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM&#xff08;Java Virtual Machine&#xff09;让"一次编写&#xff0c;到处运行"成为可能。这个软件层面的虚拟化让我着迷&#xff0c;但直到后来接触VMware和Doc…...

​​企业大模型服务合规指南:深度解析备案与登记制度​​

伴随AI技术的爆炸式发展&#xff0c;尤其是大模型&#xff08;LLM&#xff09;在各行各业的深度应用和整合&#xff0c;企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者&#xff0c;还是积极拥抱AI转型的传统企业&#xff0c;在面向公众…...