【Java】设计模式之保护性暂停

news2024/9/28 21:21:17

设计模式之保护性暂停

Guarded Suspension,这个设计模式,主要用在一个线程等待另一个线程的执行结果(发请求等待响应)

  • 有一个结果需要从一个线程传递到另一个线程,传递只进行一次,用设计模式保护性暂停。

  • 如果有结果不断从一个线程到另一个线程那么可以使用另一个设计模式消息队列(之后的知识)

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

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

想要让一个线程等待另一个线程的执行结果,可以让这两个线程都关联上同一个 Guarded 0bject保护对象,让对象的管程的waitset来充当通知的桥梁,使用对象的wait()/notify()方法来进行通知。

结果如何从线程2传到线程1?其实保护对象由开发者创建,它的属性就是response。线程1对该对象加锁并执行wait()方法,线程1首先执行陷入阻塞。线程2也对保护对象加锁,当它执行完毕之后,将结果记录在保护对象的response之中然后执行notifyAll()方法唤醒线程1。线程1被唤醒,它就可以从保护对象的response属性中拿到线程2处理的结果。


改进:

如果要多个类之间进行通信,使用保护对象不是很方便。 因此设计一个用来解耦的中间类,这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理。

注意:在这种情况下,结果等待者和结果生产者还是一一对应的。如果结果等待者和结果生产者是多对多的关系,要用到另一种设计模式——生产者/消费者。

在这里插入图片描述

需要创建一个保护对象的管理器来管理多个保护对象。

package org.example;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Hashtable;
import java.util.Map;

/**
 * @author xcy
 */

public class Main {
    static Logger logger= LoggerFactory.getLogger("test");

    public static void main(String[] args) throws InterruptedException {
        //结果等待者
        //这个for循环创建了5个保护对象,创建对象的方法确实是原子性的,但是这5个对象的保护对象不会和线程序号一致
        //因为5个线程并不是循环new出一个立刻就执行一个,线程的启动会有延迟
        //比如说线程1、2、3、4、5,并不是按照12345的顺序启动,会随机启动,所以最后线程名和创建出保护对象名称并不一致
        //如果想要让保护对象的序号与线程一致,要根据当前线程的序号来创建保护对象,而不是循环计数
        for (int i = 0; i < 5; i++) {
            int finalI=i;
            new Thread(() -> {
                GuardedObject guardedObject = GuardedObjectManager.createGuardedObject();
                logger.debug("开始等待结果,线程的保护对象为:"+guardedObject.getId());
                guardedObject.waitResult();
                logger.debug("等到结果:" + guardedObject.getResponse());
            },"t"+finalI).start();
        }
        Thread.sleep(1000);
        //结果生产者
        for (int i = 5; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                GuardedObject guardObject = GuardedObjectManager.getGuardObject(finalI - 5);
                System.out.println(Thread.currentThread()+"获得保护对象:"+guardObject);
                guardObject.complete(finalI);
            },"t"+finalI).start();
        }
    }
}

/**
 * 保护对象,用于两个线程之间的通信
 */
class GuardedObject {
    private Integer id;
    private Object response;

    public GuardedObject(Integer id) {
        this.id = id;
    }

    /**
     * 拥有该对象锁的线程释放对象锁,进入等待队列
     */
    public synchronized Object waitResult() {
        while (response == null) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        return this.response;
    }

    /**
     * 唤醒该对象等待队列中的线程
     */
    public synchronized void complete(Object response) {
        System.out.println("线程"+Thread.currentThread()+"返回结果"+response);
        this.response = response;
        this.notifyAll();
    }

    public Integer getId() {
        return id;
    }

    public Object getResponse() {
        return response;
    }

    @Override
    public String toString() {
        return "GuardedObject{" +
                "id=" + id +
                '}';
    }
}

/**
 * 用来管理GuardedObject
 */
class GuardedObjectManager {
    private static int id;
    /**
     * 由于hashmap是线程不安全的,所以用hashtable保证put和get的原子性
     */
    public static Map<Integer, GuardedObject> map = new Hashtable<>();

    public synchronized static GuardedObject createGuardedObject() {
        System.out.println(Thread.currentThread()+":"+id);
        GuardedObject guardedObject = new GuardedObject(id);
        map.put(id, guardedObject);
        id++;
        System.out.println(Thread.currentThread()+":"+id);
        return guardedObject;
    }

    public static GuardedObject getGuardObject(int id) {
        return map.get(id);
    }
}

结果:

在这里插入图片描述

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

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

相关文章

使用pagehelper插件进行分页查询

一、导入mybatis和pagehelper坐标 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.0</version> </dependency> <dependency><groupId&…

项目管理进阶之PDCA

前言 项目管理进阶系列&#xff0c;今天开始发布第一篇喽。 博主其实一直在构思&#xff0c;如何开启这个系列&#xff0c;但是我们通常项目管理讲的“五大过程十大领域”&#xff0c;往往太书面了。因此尝试从中抓取几个核心&#xff0c;以供有志之士参考。 那么&#xff0c…

史诗级长文--决策树

决策树 决策树(decision tree)是一种基本的分类与回归方法。 举个通俗易懂的例子&#xff0c;如下图所示的流程图就是一个决策树&#xff0c;长方形代表判断模块(decision block)&#xff0c;椭圆形成代表终止模块(terminating block)&#xff0c;表示已经得出结论&#xff0c;…

基于商品列表的拖拽排序后端实现

目录 一&#xff1a;实现思路 二&#xff1a;实现步骤 二&#xff1a;实现代码 三&#xff1a;注意点 一&#xff1a;实现思路 后台实现拖拽排序通常需要与前端进行配合&#xff0c;对商品的列表拖拽排序&#xff0c;前端需要告诉后端拖拽的元素和拖动的位置。 这里我们假…

Java多线程技术11——ThreadPoolExecutor类的使用1

1 概述 ThreadPoolExecutor类可以非常方便的创建线程池对象&#xff0c;而不需要程序员设计大量的new实例化Thread相关的代码。 2 队列LinkedBlockingQueue的使用 public class Test1 {public static void main(String[] args) {LinkedBlockingQueue queue new LinkedBlocki…

Activity启动流程

早就想写这个笔记用于记录这段知识&#xff0c;但是碍于太过庞大所以始终没有进行这段知识的整理 很多博客喜欢画一个时序图展示所有的流程&#xff0c;但是过于庞大&#xff0c;看起来有点吃力&#xff0c;这里我们画多个时序图来展示这个流程 1.app请求AMS启动Activity 在前…

特征工程筛选重要变量

特征筛选主要分为3个方法&#xff1a;过滤法、嵌入法&#xff08;经典的一些树模型比如xgboost&#xff09;、包裹法&#xff08;经典的RFECV&#xff0c;RFE递归特征消除法&#xff09; 过滤法更快速&#xff0c;但更粗糙。 包装法和嵌入法更精确&#xff0c;比较适合具体到算…

P1262 间谍网络

1、思路 阅读题目&#xff0c;发现有些间谍可以是被前面的点更新&#xff0c;也就是说&#xff0c;在一开始的时候&#xff0c;把能贿赂的人员从小到达排个序&#xff0c;再使用bfs算法&#xff0c;把他们能到达的人员的贿赂价钱设置为0。 有解的情况&#xff1a; 首先如果有…

JavaScript:Date 对象-时间日期

Date 对象-时间日期: - JS中所有的关于时间信息都需要通过Date对象来表示 // 创建一个Date对象 // 如果直接使用new Date()创建时间对象&#xff0c;它会默认创建一个表示代码执行时刻的对象var d new Date();// 如果希望创建一个指定的时间的Date的对象&#xff0c;需要传递…

python总结-装饰器

装饰器 装饰器解决日志问题&#xff08;分三个版本&#xff09;多个装饰器带参数的装饰器wraps装饰器内置装饰器property装饰器staticmethod装饰器classmethod装饰器 类装饰器缓存装饰器和计时装饰器综合练习 概念 装饰器来自 Decorator 的直译。什么叫装饰&#xff0c;就是装点…

【tkinter 电子时钟 实现时间日期 可实现透明 无标题栏】

下面是一个使用tkinter实现的简单的电子时钟&#xff0c;包括时间和日期的显示。该窗口是透明的&#xff0c;没有标题栏。 效果&#xff1a; import tkinter as tk from datetime import datetimedef update_time():now datetime.now()time_label.configure(textnow.strftim…

【一】使用vue-cli创建vue3的helloworld项目

不再推荐使用vue-cli命令创建vue3的项目&#xff0c;vue-cli 是 Vue 早期推出的一款脚手架&#xff0c;使用 webpack 创建 Vue 项目。后期推荐使用 create-vue&#xff0c;create-vue 是 Vue3 的专用脚手架&#xff0c;使用 vite 创建 Vue3 的项目(关注【二】使用create-vue创建…

操作系统期末复习笔记(持续更新..)

一、操作系统的基本概念 1.1 操作系统概念 控制和管理整个计算机系统的硬件与软件资源。合理地组织、调度计算机的工作与资源。为用户和其他软件提供方便接口与环境的程序集合。 1.2 操作系统的特征 特征&#xff1a;并发&#xff0c;共享&#xff0c;虚拟&#xff0c;异步…

每日算法打卡:数的三次方根 day 7

文章目录 原题链接题目描述输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a; 题目分析示例代码 原题链接 790. 数的三次方根 题目难度&#xff1a;简单 题目描述 给定一个浮点数 n&#xff0c;求它的三次方根。 输入格式 共一行&#xff0c;包含一个浮…

如何将手机中termux用电脑的vnc显示

在电脑中我们同样需要下载 vnc 这里填写手机上的 IP&#xff1a;端口号 我的是 10.11.166.219:5902 下面填名字然后 手机端 输入sshd开始ssh这边就可以连接啦

【ARM 处理器】程序存储详解

本篇文章主要介绍ARM处理器&#xff0c;Code, RO-data,RW-data,ZI-data 知识以及程序存储情况 目录 1. 专业词汇2. 程序存储3. 程序空间计算 1. 专业词汇 Code &#xff1a; 代码区&#xff0c;存储在 ROM 区域RO-data&#xff1a;Read Only data&#xff0c;即只读数据域&…

2024年虚拟DOM技术将何去何从?

从诞生之初谈起&#xff0c;从命令式到声明式&#xff0c;Web开发的演变之路 Web开发的起源与jQuery的统治 在Web开发的早期阶段&#xff0c;操作DOM元素主要依赖命令式编程。当时&#xff0c;jQuery因其易用性而广受欢迎。使用jQuery&#xff0c;开发者通过具体的命令操作DOM&…

qt自定义控件的封装

刚学了一个很有意思的东西,前面学了list,Tree,Table三大控件和一部分常用基础控件,但感觉没啥意思,就是用别人的直接用,刚学了一个自定义控件的封装,流程如下: 想把两个不相关的组件封装在一块,直接用ui不行,所以先新添加了qt设计师页面,新添加了一个SmallWidget *ui 在smal…

FCN-8s源码理解

FCN网络用于对图像进行分割&#xff0c;由于是全卷积网络&#xff0c;所以对输入图像的分辨率没有要求。本文重点对fcn8s.py中图像降采样和上采样后图像分辨率的变换进行理解。 相关知识 为准确理解图像分辨率的变换&#xff0c;对网络结构中影响图像分辨率变换的几个函数进行…

Linux基础命令@echo、tail、重定向符

目录 echo概念语法作用演示一演示二 反引号作用 tail概念语法作用不带选项&#xff0c;演示一带选项 -num&#xff0c;演示二带选项 -f &#xff0c; 持续跟踪 重定向符概念作用覆盖重定向&#xff0c;>演示一演示二 追加重定向&#xff0c;>>演示一演示二 总结 echo …