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,用在一个线程等待另一 个线程的执行结果 要点 ● 有一个结果需要从一个线程传递到另一 个线程,让他们关联同一一个GuardedObject ● 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(生产者…...
HTTPS加密解析
日升时奋斗,日落时自省 目录 1、加密解释 2、对称加密 3、非对称加密 4、证书 HTTPS(HyperText Transfer Protocol over Secure Socket Layer)也是一个应用层协议,是在HTTP协议的基础上引入了一个加密层 HTTP协议内容都是按…...
Python每日一练(20230309)
目录 1. 删除有序数组中的重复项 ★ 2. 二叉树的最小深度 ★★ 3. 只出现一次的数字 II ★★ 🌟 每日一练刷题专栏 C/C 每日一练 专栏 Python 每日一练 专栏 1. 删除有序数组中的重复项 给你一个有序数组 nums ,请你原地删除重复出现的元素…...
哈希表题目:数组的度
文章目录题目标题和出处难度题目描述要求示例数据范围解法思路和算法代码复杂度分析题目 标题和出处 标题:数组的度 出处:697. 数组的度 难度 4 级 题目描述 要求 给定一个非空且只包含非负数的整数数组 nums\texttt{nums}nums,数组的…...
初识rollup 打包、配置vue脚手架
rollup javascript 代码打包器,它使用了 es6 新标准代码模块格式。 特点: 面向未来,拥抱 es 新标准,支持标准化模块导入、导出等新语法。tree shaking 静态分析导入的代码。排除未实际引用的内容兼容现有的 commonJS 模块&#…...
软考网络工程师证书有用吗?
当然有用,但是拿到网络工程师证书的前提是对你自己今后的职业发展有帮助,用得到才能对你而言发挥它最大的好处。软考证书的具体用处:1.纳入我国高校人才培养和教学体系目前,软考已经被纳入高校人才培养和教学体系。在很多高校中&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. 进程管理 进程、线程基础知识 什么是进程 我们编写的代码只是一个存储在硬盘的静态文件,通过编译后就会生成二进制可执行文件,当我们运行这个可执行文件后,它会被装载到内存中,接着 CPU 会执行程序中的每一条指令,…...
安装mayavi的成功步骤
这篇文章是python 3.6版本,windows系统下的安装,其他python版本应该也可以,下载对应的包即可。 一定不要直接pip install mayavi,这个玩意儿对vtk的版本有要求。 下载whl包 搞了很久不行,咱也别费那个劲了࿰…...
vue+echarts.js 实现中国地图——根据数值表示省份的深浅——技能提升
最近在写后台管理系统,遇到一个需求就是 中国地图根据数值 展示深浅颜色。 效果图如下: 直接上代码: 1.html部分 <div id"Map"></div>2.css部分——一定要设置尺寸 #Map {width: 100%;height: 400px; }3.js部分 …...
[oeasy]python0104_指示灯_显示_LED_辉光管_霓虹灯
编码进化 回忆上次内容 x86、arm、riscv等基础架构 都是二进制的包括各种数据、指令 但是我们接触到的东西 都是屏幕显示出来的字符 计算机 显示出来的 一个个具体的字型 计算机中用来展示的字型 究竟是 如何进化的 呢?🤔🤔 模拟电路时…...
Easy Deep Learning——卷积层
为什么需要卷积层,深度学习中的卷积是什么? 在介绍卷积之前,先引入一个场景 假设您在草地上漫步,手里拿着一个尺子,想要测量草地上某些物体的大小,比如一片叶子。但是叶子的形状各异,并且草地非…...
深入分析@Bean源码
文章目录一、源码时序图二、源码解析1. 运行案例程序启动类2. 解析AnnotationConfigApplicationContext类的AnnotationConfigApplicationContext(Class<?>... componentClasses)构造方法3. 解析AbstractApplicationContext类的refresh()方法4. 解析AbstractApplicationC…...
Web Components学习(1)
一、什么是web components 开发项目的时候为什么不手写原生 JS,而是要用现如今非常流行的前端框架,原因有很多,例如: 良好的生态数据驱动试图模块化组件化等 Web Components 就是为了解决“组件化”而诞生的,它是浏…...
Element-UI实现复杂table表格结构
Element-UI组件el-table用于展示多条结构类似的数据,可对数据进行排序、筛选、对比或其他自定义操作。将使用到以下两项,来完成今天demo演示:多级表头:数据结构比较复杂的时候,可使用多级表头来展现数据的层次关系。合…...
Azure AD 与 AWS 单一帐户SSO访问集成,超详细讲解,包括解决可能出现的错误问题
本教程介绍如何将 AWS Single-Account Access 与 Azure Active Directory (Azure AD) 相集成。 将 AWS Single-Account Access 与 Azure AD 集成后,可以: 在 Azure AD 中控制谁有权访问 AWS Single-Account Access。让用户使用其 Azure AD 帐户自动登录…...
lvgl 笔记 按钮部件 (lv_btn) 和 开关部件 (lv_switch)
按钮基础使用方法: lv_btn 和 lb_obj 使用方法一样,只是外表并不相同,基础创建方法只需一行代码。 lv_obj_t* btn lv_btn_create(lv_scr_act()); 添加大小和位置: lv_obj_t* btn lv_btn_create(lv_scr_act()); lv_obj_set_s…...
Python高频面试题——生成器(最通俗的讲解)
生成器定义在 Python 中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。 在调用生成器运行的过程中…...
品牌软文怎么写?教你几招
软文是什么?软文的本质就是广告,当然不是明晃晃的推销,而是自然隐晦地植入产品信息,引导更多用户自愿下单。 品牌软文对于写手的经验、内容的质量要求都相对较高,否则写出来的软文无法达到预期的效果。品牌软文怎么写…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果