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

JUC并发编程设计模式

一、保护性暂停

1.1 定义

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

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

● 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(生产者/消费者)

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

● 因为要等待另一方的结果, 因此归类到同步模式
在这里插入图片描述

1.2 实现

GuardedObject(保护对象),其response属性用来保存最终的结果(t1使用结果,t2产生结果),初始值为null(wait-notify在GuardedObject上等待结果)

模拟应用场景:线程1需要等待线程2产生的结果,线程2进行一个下载任务

import cn.itcast.pattern.Downloader;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.List;@Slf4j(topic = "c.Test")
public class Test {public static void main(String[] args) throws InterruptedException {GuardedObject guardedObject = new GuardedObject();new Thread(() -> {// 等待结果log.debug("等待结果");List<String> list = guardedObject.get();log.debug("结果的大小:{}", list.size());}, "t1").start();new Thread(() -> {log.debug("执行下载");try {List<String> list = Downloader.download();// 将下载结果传给线程1guardedObject.complete(list);} catch (IOException e) {e.printStackTrace();}});}
}class uardedObject {// 结果private Object response;// 获取结果的方法public Object get() {synchronized (this) {// 还没有结果while (response == null) {// 调用wait等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}return response;}}// 产生结果public void complete(Object response) {synchronized (this) {// 给结果成员变量赋值this.response = response;// 通知等待线程this.notifyAll();}}
}

运行结果:
在这里插入图片描述

1.3 保护性暂停扩展—增加超时

二、 两阶段终止-interrupt

Two Phase Termination
在一个线程T1中如何“优雅”终止线程T2?这里的【优雅】指的是给T2一个料理后事的机会。

错误思路

● 使用线程对象的stop()方法停止线程(强制杀死
—— stop()方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁

● 使用System.exit(int)方法停止线程
—— 目的仅是停止一个线程,但这种做法会让整个程序都停止

2.1 两阶段终止-interrupt分析

有如下场景,做一个系统的健康状态监控(记录电脑CPU的使用率、内存的使用率)实现定时监控。实现这样一个场景,可用一个后台的监控线程不断记录。
在这里插入图片描述
代码实现

import lombok.extern.slf4j.Slf4j;@Slf4j(topic = "c.Test")
public class Test {public static void main(String[] args) throws InterruptedException {TwoPhaseTermination tpt=new TwoPhaseTermination();// 启动监控线程(每隔1秒执行监控记录)tpt.start();// 模拟非正常打断,主线程经过3.5后,被interrupt()===>优雅打断Thread.sleep(3500);tpt.stop();}
}
// 监控类代码
@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination{// 创建监控线程private Thread monitor;// 启动监控线程public void start(){// 创建线程对象monitor=new Thread(()->{// 不断被执行监控while (true){// 获取当前线程对象,判断是否被打断Thread current = Thread.currentThread();if(current.isInterrupted()){// 若被打断log.debug("料理后事");break;}// 若未被打断(每隔2s执行睡眠,进行监控操作)try {Thread.sleep(1000);  // 情况1===>非正常打断(睡眠过程中)log.debug("执行监控记录");   // 情况2===>正常打断} catch (InterruptedException e) {e.printStackTrace();// 重新设置打断标记(sleep()被打断后会清除打断标记)current.interrupt();}}});monitor.start();}// 停止监控线程public void stop(){// "优雅"打断monitor.interrupt();}
}

运行结果
在这里插入图片描述

分析:监控线程每隔1s监控系统,主线程处于休眠状态,3.5秒后休眠状态被打断

*****interrupted()与isInterrupted()均为判断当前线程是否被打断,表面上看起来类似。但却有着很大的区别,调用isInterrupted()不会清除打断标记,而调用interrupted()判断完后会将打断标记清除

三、固定运行顺序

同步模式之顺序控制
比如,先打印2后打印1(如果不加控制两个线程被CPU调度的时间不受控制)

3.1 wait notify版

import lombok.extern.slf4j.Slf4j;@Slf4j(topic = "c.Test25")
public class Test25 {// 锁对象static final Object lock = new Object();// 表示 t2 是否运行过static boolean t2runned = false;public static void main(String[] args) {// 打印1的线程(线程1期待线程2打印过后将标记置为真后再打印)Thread t1 = new Thread(() -> {synchronized (lock) {while (!t2runned) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("1");}}, "t1");// 打印2的线程Thread t2 = new Thread(() -> {synchronized (lock) {log.debug("2");t2runned = true;lock.notify();}}, "t2");t1.start();t2.start();}
}

运行结果:
在这里插入图片描述

3.2 park unpack版

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.LockSupport;@Slf4j(topic = "c.Test26")
public class Test26 {public static void main(String[] args) {Thread t1 = new Thread(() -> {LockSupport.park();log.debug("1");}, "t1");t1.start();new Thread(() -> {log.debug("2");LockSupport.unpark(t1);},"t2").start();}
}

运行结果:
在这里插入图片描述

3.3 ReentrantLock——await&signal

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;@Slf4j(topic = "c.Test24")
public class Test24 {static final Object room = new Object();static boolean flag = false;static ReentrantLock ROOM = new ReentrantLock();// 创建一个新的条件变量(休息室)static Condition waitSet = ROOM.newCondition();public static void main(String[] args) throws InterruptedException {// 打印“1”的线程Thread t1 = new Thread(() -> {ROOM.lock();try {log.debug("2是否打印完毕[{}]", flag);while (!flag) {log.debug("未打印2,先歇会!");try {waitSet.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("1");} finally {// 解锁ROOM.unlock();}});// 打印“2”的线程Thread t2=new Thread(()->{ROOM.lock();try {log.debug("2");flag=true;// 唤醒线程waitSet.signal();}finally {ROOM.unlock();}});t1.start();t2.start();}
}

四、交替输出

线程1输出a 5次,线程2输出b 5次,线程3输出c 5次。现在要求输出abcabcabcabcabc怎么实现

4.1 wait notify版

import lombok.extern.slf4j.Slf4j;@Slf4j(topic = "c.Test")
public class Test {public static void main(String[] args) {Wait_notify wait_notify = new Wait_notify(1,5);// 线程t1打印anew Thread(() -> {wait_notify.print("a",1,2);}, "t1").start();// 线程t1打印bnew Thread(() -> {wait_notify.print("b",2,3);}, "t2").start();// 线程t3打印cnew Thread(() -> {wait_notify.print("c",3,1);}, "t3").start();}
}/*输出内容         等待标记     下一个标记a               1           2b               2           3c               3           1*/
class Wait_notify {// 等待标记【存在3个线程,因此用blooen变量不太合适(blooen变量的状态只有两个)】private int flag;        // 1: t1    2: t2   3: t3// 循环次数private int loopnumber;public Wait_notify(int flag, int loopnumber) {this.flag = flag;this.loopnumber = loopnumber;}// 打印方法(打印内容,打印标记)public void print(String s, int wait, int nextFlag) {for (int i = 0; i < loopnumber; i++) {synchronized (this) {while (flag != wait) {// 进入等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print(s);flag = nextFlag;// 唤醒其他等待的线程this.notifyAll();}}}
}

运行结果:
在这里插入图片描述

4.2 ReentrantLock——await&signal

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;@Slf4j(topic = "c.Test")
public class Test {public static void main(String[] args) throws InterruptedException {Awaitsynch awaitsynch = new Awaitsynch(5);// 线程1的休息室Condition a = awaitsynch.newCondition();// 线程2的休息室Condition b = awaitsynch.newCondition();// 线程3的休息室Condition c = awaitsynch.newCondition();new Thread(() -> {awaitsynch.print("a", a, b);}).start();new Thread(() -> {awaitsynch.print("b", b, c);}).start();new Thread(() -> {awaitsynch.print("c", c, a);}).start();// 三个线程刚开始都会进入各自休息室进行休息(利用主线程先将a休息室中的线程唤醒)Thread.sleep(1000);awaitsynch.lock();try {System.out.println("开始......");// 唤醒a休息室中的线程a.signal();} finally {awaitsynch.unlock();}}
}
class Awaitsynch extends ReentrantLock {// 循环次数private int loopnumber;public Awaitsynch(int loopnumber) {this.loopnumber = loopnumber;}//  (打印内容,进入的休息室,下一个休息室)public void print(String s, Condition con, Condition next) {for (int i = 0; i < loopnumber; i++) {// 给当前线程加锁lock();try {con.await();System.out.print(s);// 唤醒下一个休息室中的线程next.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {unlock();}}}
}

运行结果:
在这里插入图片描述

4.3 park unpack版

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.LockSupport;@Slf4j(topic = "c.Test24")
public class Test24 {static Thread a;static Thread b;static Thread c;public static void main(String[] args) throws InterruptedException {ParkUnpark parkUnpark = new ParkUnpark(5);a=new Thread(() -> {parkUnpark.print("a", b);});b=new Thread(() -> {parkUnpark.print("b", c);});c=new Thread(() -> {parkUnpark.print("c", a);});a.start();b.start();c.start();// 主线程唤醒当前暂停的线程LockSupport.unpark(a);}
}
class parkUpark {// 循坏次数private int loopNumber;public parkUpark(int loopNumber) {this.loopNumber = loopNumber;}// print(打印的内容,要唤醒的线程)public void print(String s, Thread next) {for (int i = 0; i < loopNumber; i++) {// 暂停当前线程(阻塞)LockSupport.park();System.out.println(s);// 唤醒下一个线程LockSupport.unpark(next);}}
}

相关文章:

JUC并发编程设计模式

一、保护性暂停 1.1 定义 即Guarded Suspension,用在一个线程等待另一 个线程的执行结果 要点 ● 有一个结果需要从一个线程传递到另一 个线程&#xff0c;让他们关联同一一个GuardedObject ● 如果有结果不断从一个线程到另一个线程那么可以使用消息队列&#xff08;生产者…...

HTTPS加密解析

日升时奋斗&#xff0c;日落时自省 目录 1、加密解释 2、对称加密 3、非对称加密 4、证书 HTTPS&#xff08;HyperText Transfer Protocol over Secure Socket Layer&#xff09;也是一个应用层协议&#xff0c;是在HTTP协议的基础上引入了一个加密层 HTTP协议内容都是按…...

Python每日一练(20230309)

目录 1. 删除有序数组中的重复项 ★ 2. 二叉树的最小深度 ★★ 3. 只出现一次的数字 II ★★ &#x1f31f; 每日一练刷题专栏 C/C 每日一练 ​专栏 Python 每日一练 专栏 1. 删除有序数组中的重复项 给你一个有序数组 nums &#xff0c;请你原地删除重复出现的元素…...

哈希表题目:数组的度

文章目录题目标题和出处难度题目描述要求示例数据范围解法思路和算法代码复杂度分析题目 标题和出处 标题&#xff1a;数组的度 出处&#xff1a;697. 数组的度 难度 4 级 题目描述 要求 给定一个非空且只包含非负数的整数数组 nums\texttt{nums}nums&#xff0c;数组的…...

初识rollup 打包、配置vue脚手架

rollup javascript 代码打包器&#xff0c;它使用了 es6 新标准代码模块格式。 特点&#xff1a; 面向未来&#xff0c;拥抱 es 新标准&#xff0c;支持标准化模块导入、导出等新语法。tree shaking 静态分析导入的代码。排除未实际引用的内容兼容现有的 commonJS 模块&#…...

软考网络工程师证书有用吗?

当然有用&#xff0c;但是拿到网络工程师证书的前提是对你自己今后的职业发展有帮助&#xff0c;用得到才能对你而言发挥它最大的好处。软考证书的具体用处&#xff1a;1.纳入我国高校人才培养和教学体系目前&#xff0c;软考已经被纳入高校人才培养和教学体系。在很多高校中&a…...

postgresql 自动备份 bat实现

postgres数据据备分,用cmd命令有些烦,写了个bat实现 BAT脚本中常用的注释命令有rem、@rem和:: rem、@rem和::用法都很简单,直接在命令后加上要注释的语句即可。例如下图,语言前加了rem,运行BAT时就会自动忽略这个句子。需要注释多行时,每行前面都要加上rem、@rem和::。…...

gdb:在命令行中会莫名暂停;detach-on-fork

这个没有捕获到断点的原因是,可能是多线程的问题,需要设置: set detach-on-fork off On Linux, if you want to debug both the parent and child processes, use the command: set detach-on-fork on/off on 默认设置,gdb会放弃子线程(或者父线程,受follow-fork-mode的…...

【3.9】RedisAOF日志、字符串、操作系统进程管理

4. 进程管理 进程、线程基础知识 什么是进程 我们编写的代码只是一个存储在硬盘的静态文件&#xff0c;通过编译后就会生成二进制可执行文件&#xff0c;当我们运行这个可执行文件后&#xff0c;它会被装载到内存中&#xff0c;接着 CPU 会执行程序中的每一条指令&#xff0c;…...

安装mayavi的成功步骤

这篇文章是python 3.6版本&#xff0c;windows系统下的安装&#xff0c;其他python版本应该也可以&#xff0c;下载对应的包即可。 一定不要直接pip install mayavi&#xff0c;这个玩意儿对vtk的版本有要求。 下载whl包 搞了很久不行&#xff0c;咱也别费那个劲了&#xff0…...

vue+echarts.js 实现中国地图——根据数值表示省份的深浅——技能提升

最近在写后台管理系统&#xff0c;遇到一个需求就是 中国地图根据数值 展示深浅颜色。 效果图如下&#xff1a; 直接上代码&#xff1a; 1.html部分 <div id"Map"></div>2.css部分——一定要设置尺寸 #Map {width: 100%;height: 400px; }3.js部分 …...

[oeasy]python0104_指示灯_显示_LED_辉光管_霓虹灯

编码进化 回忆上次内容 x86、arm、riscv等基础架构 都是二进制的包括各种数据、指令 但是我们接触到的东西 都是屏幕显示出来的字符 计算机 显示出来的 一个个具体的字型 计算机中用来展示的字型 究竟是 如何进化的 呢&#xff1f;&#x1f914;&#x1f914; 模拟电路时…...

Easy Deep Learning——卷积层

为什么需要卷积层&#xff0c;深度学习中的卷积是什么&#xff1f; 在介绍卷积之前&#xff0c;先引入一个场景 假设您在草地上漫步&#xff0c;手里拿着一个尺子&#xff0c;想要测量草地上某些物体的大小&#xff0c;比如一片叶子。但是叶子的形状各异&#xff0c;并且草地非…...

深入分析@Bean源码

文章目录一、源码时序图二、源码解析1. 运行案例程序启动类2. 解析AnnotationConfigApplicationContext类的AnnotationConfigApplicationContext(Class<?>... componentClasses)构造方法3. 解析AbstractApplicationContext类的refresh()方法4. 解析AbstractApplicationC…...

Web Components学习(1)

一、什么是web components 开发项目的时候为什么不手写原生 JS&#xff0c;而是要用现如今非常流行的前端框架&#xff0c;原因有很多&#xff0c;例如&#xff1a; 良好的生态数据驱动试图模块化组件化等 Web Components 就是为了解决“组件化”而诞生的&#xff0c;它是浏…...

Element-UI实现复杂table表格结构

Element-UI组件el-table用于展示多条结构类似的数据&#xff0c;可对数据进行排序、筛选、对比或其他自定义操作。将使用到以下两项&#xff0c;来完成今天demo演示&#xff1a;多级表头&#xff1a;数据结构比较复杂的时候&#xff0c;可使用多级表头来展现数据的层次关系。合…...

Azure AD 与 AWS 单一帐户SSO访问集成,超详细讲解,包括解决可能出现的错误问题

本教程介绍如何将 AWS Single-Account Access 与 Azure Active Directory (Azure AD) 相集成。 将 AWS Single-Account Access 与 Azure AD 集成后&#xff0c;可以&#xff1a; 在 Azure AD 中控制谁有权访问 AWS Single-Account Access。让用户使用其 Azure AD 帐户自动登录…...

lvgl 笔记 按钮部件 (lv_btn) 和 开关部件 (lv_switch)

按钮基础使用方法&#xff1a; lv_btn 和 lb_obj 使用方法一样&#xff0c;只是外表并不相同&#xff0c;基础创建方法只需一行代码。 lv_obj_t* btn lv_btn_create(lv_scr_act()); 添加大小和位置&#xff1a; lv_obj_t* btn lv_btn_create(lv_scr_act()); lv_obj_set_s…...

Python高频面试题——生成器(最通俗的讲解)

生成器定义在 Python 中&#xff0c;使用了 yield 的函数被称为生成器&#xff08;generator&#xff09;。跟普通函数不同的是&#xff0c;生成器是一个返回迭代器的函数&#xff0c;只能用于迭代操作&#xff0c;更简单点理解生成器就是一个迭代器。 在调用生成器运行的过程中…...

品牌软文怎么写?教你几招

软文是什么&#xff1f;软文的本质就是广告&#xff0c;当然不是明晃晃的推销&#xff0c;而是自然隐晦地植入产品信息&#xff0c;引导更多用户自愿下单。 品牌软文对于写手的经验、内容的质量要求都相对较高&#xff0c;否则写出来的软文无法达到预期的效果。品牌软文怎么写…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor

1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...

手动给中文分词和 直接用神经网络RNN做有什么区别

手动分词和基于神经网络&#xff08;如 RNN&#xff09;的自动分词在原理、实现方式和效果上有显著差异&#xff0c;以下是核心对比&#xff1a; 1. 实现原理对比 对比维度手动分词&#xff08;规则 / 词典驱动&#xff09;神经网络 RNN 分词&#xff08;数据驱动&#xff09…...

理想汽车5月交付40856辆,同比增长16.7%

6月1日&#xff0c;理想汽车官方宣布&#xff0c;5月交付新车40856辆&#xff0c;同比增长16.7%。截至2025年5月31日&#xff0c;理想汽车历史累计交付量为1301531辆。 官方表示&#xff0c;理想L系列智能焕新版在5月正式发布&#xff0c;全系产品力有显著的提升&#xff0c;每…...