设计模式——保护性暂停

news2024/11/26 14:56:55

同步模式之保护性暂停

文章目录

  • 同步模式之保护性暂停
    • 定义
    • 实现
    • 应用
    • 带超时版 GuardedObject
    • 扩展——原理之join
    • 扩展——多任务版 GuardedObject

定义

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

  • 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject
  • 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)
  • JDK 中,join 的实现、Future 的实现,采用的就是此模式
  • 因为要等待另一方的结果,因此归类到同步模式
    在这里插入图片描述

实现

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

应用

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

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

带超时版 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);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 // 3) 如果提前被唤醒,这时已经经历的时间假设为 400
 timePassed = 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);
 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...

扩展——原理之join

join的底层就是使用保护性暂停的设计模式

扩展——多任务版 GuardedObject

图中 Futures 就好比居民楼一层的信箱(每个信箱有房间编号),左侧的 t0,t2,t4 就好比等待邮件的居民,右侧的 t1,t3,t5 就好比邮递员
如果需要在多个类之间使用 GuardedObject 对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类,这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理。

在这里插入图片描述
中间用来解耦的类要维护一个集合,同时为了把多个guardedObject区分开,要加上id。

新增 id 用来标识 Guarded Object

 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) {
 // 开始时间 15:00:00
 long 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;
 // 产生唯一 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();
    }
 }

某次运行结果

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1632161.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

秋招后端开发面试题 - Java语言基础(下)

目录 Java基础下前言面试题toString() 、String.valueof()、(String)&#xff1f;hashCode() 方法&#xff1f;hashCode 和 equals 方法判断两个对象是否相等&#xff1f;为什么重写 equals 时必须重写 hashCode 方法&#xff1f;String、StringBuffer、StringBuilder?String …

VoxAtnNet:三维点云卷积神经网络

VoxAtnNet:三维点云卷积神经网络 摘要IntroductionProposed VoxAtnNet 3D Face PAD3D face point cloud presentation attack Dataset (3D-PCPA) VoxAtnNet: A 3D Point Clouds Convolutional Neural Network for 摘要 面部生物识别是智能手机确保可靠和可信任认证的重要组件。…

react 学习笔记二:ref、状态、继承

基础知识 1、ref 创建变量时&#xff0c;需要运用到username React.createRef()&#xff0c;并将其绑定到对应的节点。在使用时需要获取当前的节点&#xff1b; 注意&#xff1a;vue直接使用里面的值&#xff0c;不需要再用this。 2、状态 组件描述某种显示情况的数据&#…

[ACTF2020 新生赛]BackupFile 1 [极客大挑战 2019]BuyFlag 1 [护网杯 2018]easy_tornado 1

目录 [ACTF2020 新生赛]BackupFile 1 1.打开页面&#xff0c;叫我们去找源文件 2.想到用disearch扫描&#xff0c;发现源文件index.php.bak 3.访问这个文件&#xff0c;下载一个文件&#xff0c;用记事本打开 4.翻译php代码 5.构造payload url/?key123&#xff0c;得到fl…

【哈希】Leetcode 面试题 01.02. 判定是否互为字符重排

题目讲解 面试题 01.02. 判定是否互为字符重排 算法讲解 直观的想法&#xff1a;我们找到一个字符串的全排列&#xff0c;然后对比当前的排列是否等于另一个字符串。如果两个字符串如果互为排列&#xff0c;所以我们知道两个字符串对应的字符出现的个数相同&#xff0c;那么…

Windows 容器镜像踩坑记录

为什么研究windows容器&#xff1f;emm&#xff0c;公司需要&#xff0c;不想多说。 dotnet后端 问题描述&#xff1a; 基于mcr.microsoft.com/dotnet/aspnet:6.0镜像撰写dockerfile编译.net core后端项目后运行容器出现类库不存在问题&#xff1a; 程序中使用了fastreport&a…

编写你的第一个 golang 的应用程序

进行你的第一个golang的程序 当你把程序都安装好以后 环境变量配置 好 vscode 插件下载好以后 1. 创建一个test.go 的文件 //主包&#xff0c;可执行文件所在包 package main//导入包 import "fmt"//主函数&#xff0c;入口函数 func main() { }2.解释 需要导入包 …

Linux PTP学习

前言 本文是对Linux PTP的学习记录&#xff0c;不足之处请指出。Linux PTP用于在Linux系统的精确时钟同步&#xff0c;支持IEEE 1588 Precision Time Protocol&#xff08;PTP&#xff09;标准&#xff0c;目的是实现在网络中&#xff0c;设备之间的高精度时间同步。它是一个工…

Meta分析在生态环境领域里的应用教程

原文链接&#xff1a;Meta分析在生态环境领域里的应用教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247602936&idx4&sn50c2b3141baaa8635905fc405767d6ed&chksmfa82131fcdf59a09b57750e50657b2a06706f3b46806fc7ef5341d16701b99a14d4f7d82d3b9&am…

【算法】搜索插入位置

本题来源---《搜索插入位置》 题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1…

02.Kafka部署安装

1 Linux 安装 Kafka 1.1 安装前的环境准备 由于 Kafka 是用 Scala 语言开发的&#xff0c;运行在 JVM 上&#xff0c;因此在安装Kafka之前需要先安装JDK。 yum install java-1.8.0-openjdk* -y kafka 依赖 zookeeper&#xff0c;所以需要先安装 zookeeper。 wget https://ar…

2024深圳杯(东北三省)数学建模C题完整论文讲解(含完整python代码及所有残骸音爆位置求解结果)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024深圳杯&#xff08;东北三省数学建模联赛&#xff09;A题多个火箭残骸的准确定位完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊…

Redis哈希槽和一致性哈希

前言 单点的Redis有一定的局限&#xff1a; 单点发生故障&#xff0c;数据丢失&#xff0c;影响整体服务应用自身资源有限&#xff0c;无法承载更多资源分配并发访问&#xff0c;给服务器主机带来压力&#xff0c;性能瓶颈 我们想提升系统的容量、性能和可靠性&#xff0c;就…

HTML 学习笔记

html 超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language&#xff0c;简称&#xff1a;HTML&#xff09;是一种用于创建网页的标准标记语言。 1.HTML文档的后缀名 (1) .html (2) .htm 这里更推荐使用 ".html "&#xff0c;命名应该遵从含义清…

人脸识别系统架构

目录 1. 系统架构 1.1 采集子系统 1.2 解析子系统 1.3 存储子系统 1.4 比对子系统 1.5 决策子系统 1.6 管理子系统 1.7 应用开放接口 2. 业务流程 2.1 人脸注册 2.2 人脸验证 2.2.1 作用 2.2.2 特点 2.2.3 应用场景 2.3 人脸辨识 2.3.1 作用 2.3.2 特点 2.3.3…

常用算法代码模板 (3) :搜索与图论

AcWing算法基础课笔记与常用算法模板 (3) ——搜索与图论 常用算法代码模板 (1) &#xff1a;基础算法 常用算法代码模板 (2) &#xff1a;数据结构 常用算法代码模板 (3) &#xff1a;搜索与图论 常用算法代码模板 (4) &#xff1a;数学知识 文章目录 0 搜索技巧1 树与图的存…

网络编程——TCP

socket socket类型 流式套接字(SOCK_STREAM) TCP 提供了一个面向连接、可靠的数据传输服务&#xff0c;数据无差错、无重复、无丢失、无失序的发送且按发送顺序接收。内设置流量控制&#xff0c;避免数据流淹没慢的接收方。数据被看作是字节流&#xff0c;无长度限制。 数据报…

学习STM32第二十天

低功耗编程 一、修改主频 STM32F4xx系列主频为168MHz&#xff0c;当板载8MHz晶振时&#xff0c;系统时钟HCLK满足公式 H C L K H S E P L L N P L L M P L L P HCLK \frac{HSE \times PLLN}{PLLM \times PLLP} HCLKPLLMPLLPHSEPLLN​&#xff0c;在文件stm32f4xx.h中可修…

寝室快修|基于SprinBoot+vue的贵工程寝室快修小程序(源码+数据库+文档)

贵工程寝室快修目录 目录 基于SprinBootvue的贵工程寝室快修小程序 一、前言 二、系统设计 三、系统功能设计 1学生信息管理 2 在线报修管理 3公告信息管理 4论坛信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&a…

基于SpringBoot+Vue校园竞赛管理系统的设计与实现

项目介绍&#xff1a; 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;竞赛信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行…