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

news2024/11/15 7:44:22

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

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

可以用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.coloradmin.cn/o/1365761.html

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

相关文章

【解决方案】电能质量在线监测装置和防孤岛保护装置在特斯拉工厂分布式光伏项目的应用

摘要&#xff1a; 随着全球对可再生能源的关注度不断提高&#xff0c;分布式光伏发电系统在近年来得到了广泛应用。分布式光伏发电系统具有环保、灵活等优势&#xff0c;能够有效地缓解能源短缺和环境污染问题。同时&#xff0c;电能质量在线监测装置和防孤岛保护装置在分布式…

leetcode:412. Fizz Buzz(python3解法)

难度&#xff1a;简单 给你一个整数 n &#xff0c;找出从 1 到 n 各个整数的 Fizz Buzz 表示&#xff0c;并用字符串数组 answer&#xff08;下标从 1 开始&#xff09;返回结果&#xff0c;其中&#xff1a; answer[i] "FizzBuzz" 如果 i 同时是 3 和 5 的倍数。a…

开心自走棋:使用 Laf 云开发支撑数百万玩家

先介绍一下开心自走棋 开心自走棋是一款剑与魔法的烧脑自走棋游戏。以著名的魔幻世界观为蓝本&#xff0c;采用了轻松可爱的画面风格&#xff0c;精致细腻的动画和特效来还原魔兽之战。 现在市面上自走棋游戏多是 PvP 玩法为主&#xff0c;而开心自走棋是以 PvE 玩法为主的&a…

刷了四百道算法题,我在项目里用过哪几道呢?

大家好&#xff0c;我是老三&#xff0c;今天和大家聊一个话题&#xff1a;项目中用到的力扣算法。 不知道从什么时候起&#xff0c;算法已经成为了互联网面试的标配&#xff0c;在十年前&#xff0c;哪怕如日中天的百度&#xff0c;面试也最多考个冒泡排序。后来&#xff0c;…

VTK将二维图像向三维空间中无参数化的曲面表面进行纹理映射(含代码)

实现纹理映射主要是建立纹理空间与模型空间、模型空间与屏幕空间之间的映射关系(见图 6-28)&#xff1a; 其中纹理空间可以定义为u-v 空间&#xff0c;每个轴标范围为 (0.1)。其中对于一个纹理图像&#xff0c;其左下角 v 标为 0.0)&#xff0c;右上角标为 1.1)。而对于简单的参…

假设与灵敏度分析

灵敏度分析 关系究竟有多敏感&#xff0c;就要进行灵敏度分析 如果你改变了系统参数后&#xff0c;引起这个模型&#xff08;公式&#xff09;输出的变化的程度不大&#xff0c;则说明你的模型稳定性较强&#xff08;即灵敏性较差&#xff09;&#xff0c;反之则反&#xff01…

【Python期末】动态爬取电影Top250数据可视化处理(有GUI界面/无数据库)

诚接计算机专业编程作业(C语言、C、Python、Java、HTML、JavaScript、Vue等)&#xff0c;10/15R左右&#xff0c;如有需要请私信我&#xff0c;或者加我的企鹅号&#xff1a;1404293476 本文资源&#xff1a;https://download.csdn.net/download/weixin_47040861/88713693 目录…

简析云能耗管理系统在某高校建筑系统平台的设计与应用

叶根胜 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;根据本项目&#xff0c;依托某学院电能计量管理系统、供水计量监督系统、供热计量管理系统等基础平台&#xff0c;制定了高校建筑能耗综合管理系统平台应用的总体框架和方案。该系统可以实时监控、统计能耗和…

https配置证书

HTTPS 基本原理 https 介绍 HTTPS&#xff08;全称&#xff1a;HyperText Transfer Protocol over Secure Socket Layer&#xff09;&#xff0c;其实 HTTPS 并不是一个新鲜协议&#xff0c;Google 很早就开始启用了&#xff0c;初衷是为了保证数据安全。 国内外的大型互联网…

STM32CubeMX教程20 SPI - W25Q128驱动

目录 1、准备材料 2、实验目标 3、实验流程 3.0、前提知识 3.1、CubeMX相关配置 3.1.1、时钟树配置 3.1.2、外设参数配置 3.1.3、外设中断配置 3.2、生成代码 3.2.1、外设初始化调用流程 3.2.2、外设中断调用流程 3.2.3、添加其他必要代码 4、常用函数 5、烧录验…

互联网干洗店洗鞋店搭建一套私域小程序有哪些优势?

在快节奏的现代生活中&#xff0c;我们常常面临衣物堆积如山、时间却捉襟见肘的困境。 干洗店在中国各大城市随处可见&#xff0c;假如每位顾客每月都需要一套干洗服务&#xff0c;那么一个50万人口的城市每月就有50万套干洗需求。若每家店日均处理100套衣物&#xff0c;那么至…

AE (4)_ 直方图调整的理论

#灵感# 在短暂的高通平台调试中&#xff0c;很看重直方图调整的理解。后来其它平台&#xff0c;不怎么调整这个了。但还是记录一下。 我个人还是倾向 招式简单&#xff0c;但应用到极致。 绝大部分内容来自&#xff1a;刘斯宁&#xff0c;Image Enhancement - CLAHE - 知乎 (z…

图论算法(数学建模)算法以后更新

无权值&#xff0c;无向&#xff0c;当成1就行 有向 有向赋权 顶点度的概念 Dijkstra算法 Dijkstra算法能求-一个顶点到另一-顶点最短路径。它是由Di jkstra于1959年提出的。实际它能出始点到其它所有顶点的最短路径Dijkstra算法是一种标号法:给赋权图的每一一个顶点记一个数&a…

特斯拉难挽倒退?比亚迪为中国汽车市场改写历史

对于电动汽车这个新兴产业&#xff0c;特斯拉长期以来一直处于领头羊的位置&#xff0c;近年来也面临诸多测试。去年底欧洲报道特斯拉在瑞典遭遇罢工冲击&#xff0c;运营陷入诸多困扰&#xff0c;实在出人意料。更让人讶异的是&#xff0c;年终宣布新王者比亚迪在全球销量首次…

【前端设计】文字聚光灯

欢迎来到前端设计专栏&#xff0c;本专栏收藏了一些好看且实用的前端作品&#xff0c;使用简单的html、css语法打造创意有趣的作品&#xff0c;为网站加入更多高级创意的元素。 案例 文字聚光灯效果可以用于网站标题 html <!DOCTYPE html> <html lang"en&quo…

Unity组件开发--短连接HTTP

1.网络请求管理器 using LitJson; using Cysharp.Threading.Tasks; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Events;using System.Web; using System.Text; using Sy…

SSM框架学习笔记01 | 注解开发

文章目录 1. 注解形式定义bean2.纯注解开发3.bean管理4. 依赖注入5. 第三方bean管理总结 1. 注解形式定义bean Compoenet ControllerServiceRepository 配合代码块 <context:component-scan /> 使用 2.纯注解开发 Configuration ComponentScan AnnotationConfigApplicati…

【开源】基于JAVA的教学过程管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 教师端2.2 学生端2.3 微信小程序端2.3.1 教师功能如下2.3.2 学生功能如下 三、系统展示 四、核心代码4.1 查询签到4.2 签到4.3 查询任务4.4 查询课程4.5 生成课程成绩 六、免责说明 一、摘要 1.1 项目介绍 基于JAVAVu…

国科大计算机体系结构期末考试——更新中

题型一、第二章的画图 给一个逻辑表达式&#xff0c;画出晶体管级别的电路图 cmos电路的基本电路&#xff1a; 与非门的功能是对多个输入信号进行逻辑与操作&#xff0c;然后对结果进行取反。 或非门的功能是对多个输入信号进行逻辑或操作&#xff0c;然后对结果进行取反。 …

链表:两个一组,反转链表

1、针对单链表&#xff0c;当我们进行操作时&#xff0c;如果需要进行反转或者进行其他操作时&#xff0c;有链表断开的情况&#xff0c;不妨考虑下使用辅助指针来记录断开后的链表位置&#xff0c;将需要处理的数据处理好后&#xff0c;可以使用此辅助指针找到链表的位置 #in…