【Java】设计模式之顺序控制

实际开发中,有时候一些场景需求让多个线程按照固定的顺序依次执行。这个时候就会使用这种模式。

这种模式说白了,就是给线程设定不同的条件,不符合条件的话,就算线程拿到锁也会释放锁进入等待;符合条件才让线程拿到锁能够执行代码,完毕之后再唤醒所有等待中的线程。

可以用wait/notify进行解决。让每个线程需要满足一定条件(顺序)才能执行,否则放开锁进入等待。

比如,两个线程交替打印奇偶数。

public static void main(String[] args) throws InterruptedException {//两个线程交替打印奇偶数int[] num={0};new Thread(()->{synchronized (num){while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//打印偶数if(num[0]%2==0){logger.debug("{}",num[0]);num[0]++;}else{try {num.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}num.notify();}}}).start();new Thread(()->{synchronized (num){while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//打印奇数if(num[0]%2!=0){logger.debug("{}",num[0]);num[0]++;}else{try {num.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}num.notify();}}}).start();
}

如果有多个线程进行顺序打印,使用ReentrantLock的条件变量、await、sigal会能比synchronized获得更好的性能,因为synchronized每次都唤醒所有的线程。比如说有n个线程,让n个线程循环依次打印n个数字,这时候可以让ReentrantLock创建n个条件变量,每个线程对应一个条件变量。当线程t发现还没轮到自己打印(不满足条件),就让线程t进入t-1条件变量进行等待;当线程t打印数字成功,就可以让条件变量t唤醒t+1的线程。

比如,让n条线程循环打印0-n的数字:

public static void main(String[] args) throws InterruptedException {//让n条线程循环依次打印0-n的数字int n=7;int[] num={0};ReentrantLock lock=new ReentrantLock();Condition[] conditions=new Condition[n];for (int i = 0; i < n; i++) {conditions[i]=lock.newCondition();}for (int i = 0; i < n; i++) {int finalI=i;new Thread(()->{lock.lock();try{while (true){if(num[0]==finalI){Thread.sleep(100);//轮到自己打印了logger.debug("{}",num[0]);num[0]++;if(num[0]==n){num[0]=0;}//打印完成,唤醒在当前条件变量中等待的线程(第i+1条线程)conditions[finalI].signal();}else{//没有轮到自己打印,释放锁,进入i-1的条件变量进行等待if(finalI==0){conditions[n-1].await();}else {conditions[finalI-1].await();}}}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}},"线程"+i).start();}
}

结果:

在这里插入图片描述

分析:假设要让1000条线程循环依次打印从0到1000的数字

  • 使用ReentrantLock
    • 最坏的情况下,最开始线程0抢到了锁,打印出0。第0、3-999条线程在第2条线程之前竞争到锁但是没法打印进入等待,第2条线程最后打印出2并唤醒在条件变量2中等待的线程3,此时只有线程2和线程3竞争锁,但是线程2会因为无法打印进入线程1的条件变量中等待。依此类推,每次都只有两条线程竞争锁
  • 使用synchronized
    • 最坏的情况下,最开始线程0抢到了锁,打印出0。第0、3-999条线程在第2条线程之前竞争到锁但是没法打印进入等待,第2条线程最后打印出2并唤醒其他999条线程。线程3在最坏的情况下依然是最后一个竞争到锁,然后再次唤醒999条线程……。每次都有1000条线程参与竞争锁

以上的分析可以很明显看出来,ReentrantLock的条件变量对于提升性能是有巨大的优势的。当线程数量越多,性能差距越明显。

另外也可以用park/unpark实现,因为这两个方法也能让线程阻塞、唤醒线程。park和unpark没有对象锁的概念,也没有什么等待队列,它以线程为单位阻塞和唤醒线程。所以,想要按顺序执行,一开始让所有线程都停下来,接下来手动触发一个线程运行,并让这个线程只运行一次就结束或阻塞,并触发下一个线程运行,以此类推就能按顺序进行线程的执行。

park/unpark是最简洁的。

public class Main {static Logger logger = LoggerFactory.getLogger("main");static Thread t1;static Thread t2;static Thread t3;public static void main(String[] args) throws InterruptedException {//让线程1、2、3依次顺序打印a、b、c五次int loop=5;t1 = new Thread(() -> {for (int i = 0; i < loop; i++) {LockSupport.park();logger.debug("a");LockSupport.unpark(t2);}});t2 = new Thread(() -> {for (int i = 0; i < loop; i++) {LockSupport.park();logger.debug("b");LockSupport.unpark(t3);}});t3 = new Thread(() -> {for (int i = 0; i < loop; i++) {LockSupport.park();logger.debug("c");LockSupport.unpark(t1);}});t1.start();t2.start();t3.start();LockSupport.unpark(t1);}}

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

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

相关文章

重生奇迹mu中的智力妹妹都能召唤出哪些宠物呢?

身为重生奇迹mu中的智力妹妹&#xff0c;其实是女性玩家最爱的一种职业&#xff0c;因为她是一种辅助职业&#xff0c;不需要直接参与到战役之中&#xff0c;只需躲藏在队友身后&#xff0c;提供各种BUFF的支援&#xff0c;就能充分发挥其作用&#xff0c;而且身为团队中的唯一…

Redis Geo:掌握地理空间数据的艺术

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Redis Geo&#xff1a;掌握地理空间数据的艺术 前言Redis Geo基本概念Geo模块的目的工作原理地理坐标系统 GEO的分值1. 经纬度范围2. 二分编码3. Base32编码4. 精度为什么使用Geohash&#xff1f; GEO…

Axure RP Extension For Chrome 插件安装

1. 下载好 AXURE RP EXTENSION For Chrome 插件之后解压成文件夹 2. 打开浏览器&#xff0c;找到设置--更多工具--扩展程序--加载已加压的扩展程序&#xff0c;选择解压好的文件夹 3. 点击详细信息&#xff0c;打开访问网址权限

I.MX6ULL_Linux_驱动篇(52)linux CAN驱动

CAN 是目前应用非常广泛的现场总线之一&#xff0c;主要应用于汽车电子和工业领域&#xff0c;尤其是汽车领域&#xff0c;汽车上大量的传感器与模块都是通过 CAN 总线连接起来的。 CAN 总线目前是自动化领域发展的热点技术之一&#xff0c;由于其高可靠性&#xff0c; CAN 总线…

【性能测试】JMeter分布式测试及其详细步骤

性能测试概要 性能测试是软件测试中的一种&#xff0c;它可以衡量系统的稳定性、扩展性、可靠性、速度和资源使用。它可以发现性能瓶颈&#xff0c;确保能满足业务需求。很多系统都需要做性能测试&#xff0c;如Web应用、数据库和操作系统等。 性能测试种类非常多&#xff0c…

< Linux >缓冲区

在上一篇文件的重定向&#xff0c;通常会涉及文件描述符的操控。文件描述符1&#xff08;fd 1&#xff09;通常代表着标准输出&#xff08;stdout&#xff09;&#xff0c;它默认是指向用户的终端或控制台。当执行文件重定向操作时&#xff0c;如果我们关闭文件描述符1&#xf…

AUTOSAR Builder—符合AUTOSAR(CPAP)的嵌入式系统设计工具

产品概述 AUTOSAR Builder是达索旗下一款基于Eclipse并使用Artop的可扩展工具套件。Artop是由AUTOSAR成员和合作伙伴共同推动的开放的AUTOSAR工具环境。它使用户能够构建自己的工具并与其他工具供应商进行集成。AUTOSAR Builder在此基础上新增了多个工具套件&#xff0c;更加能…

华为HarmonyOS 创建第一个鸿蒙应用 运行Hello World

使用DevEco Studio创建第一个项目 Hello World 1.创建项目 创建第一个项目&#xff0c;命名为HelloWorld&#xff0c;点击Finish 选择Empty Ability模板&#xff0c;点击Next Hello World 项目已经成功创建&#xff0c;接来下看看效果 2.预览 Hello World 点击右侧的预…

INT201 形式语言与自动机笔记(下)

L6 Context-Free Languages 上下文无关语言 Context-Free Grammar (CFG) 是一组用于生成字符串模式的递归规则。上下文无关的语法可以描述所有的常规语言&#xff0c;但它们不能描述所有可能的语言。 e.g 遵循这些规则&#xff0c;我们可以生成一种语言: 上下文无关文法 Co…

热钱涌向线控底盘!XYZ全栈集成引领新风向

在车身、底盘部分&#xff0c;中央计算区域控制带动传统车控、底盘及动力控制ECU市场迎来新一轮技术升级和域融合窗口期。线控制动、转向及空气悬架&#xff0c;正在加速与智能驾驶融合并进一步提升驾乘体验。 12月13-15日&#xff0c;2023&#xff08;第七届&#xff09;高工…

树莓派3B+ /+ CSI摄像头 + FFmpeg + SRS 实现直播推流

简介&#xff1a; 手头有一个树莓派3B 和一块CSI摄像头&#xff0c;想要实现一个推拉流直播的效果。 所需材料&#xff1a;开发板&#xff08;我用的是树莓派3B&#xff09;、CIS摄像头、云服务器&#xff08;用来搭建SRS服务器&#xff09; 具体实现思路&#xff1a; 使用…

[排序算法] 如何解决快速排序特殊情况效率低的问题------三路划分

前言 在[C/C]排序算法 快速排序 (递归与非递归)一文中,对于快速排序的单趟排序一共讲了三种方法: hoare、挖坑法、双指针法 ,这三种方法实现的快速排序虽然在一般情况下效率很高,但是如果待排序数据存在大量重复数据,那这几种方法的效率就很低,而为了解决快速排序在这样特殊情况…

XCode Build报错

XCode Build时报以下错误 B/BL out of range 143266484 (max /-128MB) 错误提示表明生成的机器代码太大&#xff0c;超出了限制 需要在XCode工程中添加宏定义&#xff0c;使得生成的可执行文件超过限制 步骤&#xff1a; 在项目设置页面中&#xff0c;选择 “Build Settings…

labelme的安装

首先尝试在(openmmlab)的python3.8的环境下安装&#xff08;失败&#xff09;。应该是我环境其他部分不对&#xff0c;和python版本应该没什么关系。&#xff08;后续&#xff0c;创建新的环境后成功&#xff0c;可直接看最后一部分。&#xff09; 首先安装是没问题的 pip in…

浅谈智能照明系统调试阶段节能方案的探究与产品选型

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201801 【摘要】针对当今智能照明系统调试完成前能源浪费的问题&#xff0c;本文结合工程案例&#xff0c;分析研究了智能照明系统调试阶段的节能方法&#xff0c;提出了采用时间控制器来解决能源及人工浪费等问题的方式。实践证明&a…

基于spark的个性化招聘推荐系统

介绍 本就业推荐系统是一个基于Spark框架的个性化推荐平台&#xff0c;使用Python Django框架、Vue和Element-Plus UI组件库构建而成。该系统通过Scrapy爬虫框架抓取招聘网站的职位数据&#xff0c;用户可以根据关键词查询符合条件的职位信息&#xff0c;同时还提供了基于协同…

QT应用篇:QT自定义最小化托盘显示和操作

将应用程序最小化到托盘任务栏中,可以使用Qt框架中的QSystemTrayIcon类。该类允许应用程序在关闭窗口后最小化到系统托盘,保持在后台运行,同时可以显示应用程序图标、添加右键菜单功能以及发送消息通知等。通过学习这些技术,能够为自己的Qt应用程序增加更多的交互性和便利性…

关于Js深拷贝的三种方法详细讲解

目录 前言 一、pandas是什么&#xff1f; 二、使用步骤 1.利用函数递归来实现深拷贝 2.利用引入lodash包 3.利用JSON字符串转换 总结 前言 当涉及到JavaScript数据拷贝的时候&#xff0c;深拷贝是一个非常关键的概念。在JavaScript中&#xff0c;对象和数组被认为是引用类型&a…

结构体(structure)的认识

前言——————希望现在在努力的各位都能感动以后享受成功的自己&#xff01; 今天我们来了解了解一下结构体&#xff0c;结构体又有什么奥妙呢&#xff0c;废话不多说&#xff0c;何为结构体呢&#xff1f;------->结构是⼀些值的集合&#xff0c;这些值称为成员变量。结…

每日算法打卡:子矩阵的和 day 8

文章目录 原题链接题目描述输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a; 题目分析示例代码 原题链接 796. 子矩阵的和 题目难度&#xff1a;简单 题目描述 输入一个 n 行 m 列的整数矩阵&#xff0c;再输入 q 个询问&#xff0c;每个询问包含四个整数…