java多线程 线程交替执行(同步)的各种实现方案

news2024/11/24 20:57:21

目录

  • java多线程 线程交替执行(同步)的各种实现方案
    • 需求
  • 指定具体执行顺序
    • 实现一:wait_notify机制 + Thread标志位
    • 实现二:lock_condition机制 + Thread标志位
    • 实现三:semaphore信号量
  • 不指定具体执行顺序,只交替执行
    • 实现一:synchronized和wait、notify实现
    • 实现二:lock和condition实现
  • 总结

java多线程 线程交替执行(同步)的各种实现方案

需求

有两个线程,A线程内容是输出数字1到26,B线程内容是输出字母a-z,

要求A B 线程交替执行,最终输出如下图所示

在这里插入图片描述

指定具体执行顺序

要求A B 线程交替执行,并且必须是A线程先执行,B后执行

解决思路:初始指定一个标志位,标记哪个线程可以运行,只要当前标志位不是当前线程(不满足运行条件),就一直等待

伪代码

线程A{
	while(满足当前线程的运行条件)
		不满足,则直接释放锁并进入wait状态
	执行当前线程内容
	通知下一个执行的线程,让其运行条件为true
}

实现一:wait_notify机制 + Thread标志位

实现思路:

  • 用标志位Thread标识哪个先执行
  • 线程A:只要标志位不是A就不满足运行条件,就等待;否则才执行内容,并切换Thread为下一个要执行的线程B
  • 线程A:只要标志位不是B就不满足运行条件,就等待;否则才执行内容,并切换Thread为下一个要执行的线程A

拓展:如果是三个线程交替,只需要让B线程执行完后把threadName标记为C,然后让C线程执行完后把threadName标记为A即可

public class AlternateThreads {

    private static final Object lock = new Object();
    private static String threadName = "A";

    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            synchronized (lock) {
                for (int i = 1; i <= 26; i++) {
                    //threadName只要不是线程A,就等待
                    while (threadName!="A") {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print(i + " ");
                    threadName = "B";
                    lock.notify();
                }
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (lock) {
                for (char c = 'a'; c <= 'z'; c++) {
                    //threadName只要不是线程A,就等待
                    while (threadName!="B") {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print(c + " ");
                    threadName = "A";
                    lock.notify();
                }
            }
        });

        threadA.start();
        threadB.start();
    }
}

实现二:lock_condition机制 + Thread标志位

实现思路:和wait_notiy实现的基本一致,也是,只不过wait变成了await,notify变成了signal

拓展:如果是三个线程交替,只需要让B线程执行完后把threadName标记为C,然后让C线程执行完后把threadName标记为A即可

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AlternateThreads {

    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();
    private static String threadName = "A";

    /**目标:交替执行的基础上,必须先执行A,再执行B
     * 方案:lock_condition机制 + Thread标志位
     * 实现思路:和我wait_notiy实现的基本一致,也是,只不过wait变成了await,notify变成了signal
     * @param args
     */
    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            try {
                lock.lock();
                for (int i = 1; i <= 26; i++) {
                    while (threadName!="A") {
                        condition.await();
                    }
                    System.out.print(i + " ");
                    threadName = "B";
                    condition.signal();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        Thread threadB = new Thread(() -> {
            try {
                lock.lock();
                for (char c = 'a'; c <= 'z'; c++) {
                    while (threadName!="B") {
                        condition.await();
                    }
                    System.out.print(c + " ");
                    threadName = "A";
                    condition.signal();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        threadA.start();
        threadB.start();
    }
}

实现三:semaphore信号量

实现思路:

  • 线程A:开始执行时,先申请信号量s1,执行结束后,再释放信号量s2,让B结束等待;
  • 线程B:开始执行时,先申请信号量s2,执行结束后,再释放信号量s1,让A结束等待;
  • 初始化:设置线程A对应的信号量初始为1,B线程对应信号量为0,保证A先执行

拓展:如果是三个线程交替,只需要让B线程执行完后release信号量C,然后让C线程执行完后release信号量A

public class AlternateThreads {

    private static Semaphore semaphoreA = new Semaphore(1);
    private static Semaphore semaphoreB = new Semaphore(0);

    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 26; i++) {
                try {
                    semaphoreA.acquire();//申请信号量A
                    System.out.print(i + " ");
                    semaphoreB.release();//通知B可以运行了
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread threadB = new Thread(() -> {
            for (char c = 'a'; c <= 'z'; c++) {
                try {
                    semaphoreB.acquire();//申请信号量B
                    System.out.print(c + " ");
                    semaphoreA.release();//通知A可以运行了
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        threadA.start();
        threadB.start();
    }
}

不指定具体执行顺序,只交替执行

要求:如果不要求A和B哪个先执行,只要求A和B交替执行

解决思路:保证互斥,直接执行内容,再notify唤醒其它线程,最后自己再释放锁进入wait状态

伪代码

线程A{
	while(是否满足当前线程A的运行条件?)
		不满足,则直接释放锁并进入wait
	执行当前线程内容
	通知下一个执行的线程B,让其运行条件为true
}

为什么这样不能保证哪个先执行?

因为各个线程只要竞争到锁,就会直接运行,后续再wait,而不是先判断是否要wait

实现一:synchronized和wait、notify实现

public class ThreadSyn {
    //实现方式一,synchronized和wait、notify实现,不保证先执行哪一个
    public static void test1() {
        Object lock = new Object();
        boolean flag = false;
        Thread a = new Thread(() -> {
            synchronized (lock) {
                for (int i = 1; i <= 26; i++) {
                    //直接运行
                    System.out.print(i + " ");
                    try {
                        //先唤醒,再阻塞
                        lock.notify();
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                lock.notify();//结束后唤醒,防止死锁
            }
        });
        Thread b = new Thread(() -> {
            synchronized (lock) {
                for (char ch = 'a'; ch <= 'z'; ch++) {
                    //直接运行
                    System.out.print(ch + " ");
                    try {
                        //先唤醒,再阻塞
                        lock.notify();
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                lock.notify();//结束后唤醒,防止死锁
            }
        });
        a.start();
        b.start();
    }

实现二:lock和condition实现

    //实现方式二:lock和condition实现,A B交替执行,也不确定先执行的是哪个,只能确保轮流执行
    public static void test2() {
        Lock lock = new ReentrantLock();
        Condition condition_A = lock.newCondition();
        Condition condition_B = lock.newCondition();
        Thread a = new Thread(() -> {
            lock.lock();
            for (int i = 1; i <= 26; i++) {
                //直接运行
                System.out.print(i + " ");
                try {
                    //先唤醒,再阻塞
                    condition_B.signal();//唤醒线程B
                    condition_A.await();//等待条件A
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            condition_B.signal();//执行完for循环后需释放最后在等待的B线程,防止死锁
            lock.unlock();

        });
        Thread b = new Thread(() -> {
            lock.lock();
            for (char ch = 'a'; ch <= 'z'; ch++) {
                System.out.print(ch + " ");//执行线程操作
                try {
                    condition_A.signal();//唤醒线程A
                    condition_B.await();//等待条件B
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            condition_A.signal();//执行完for循环后需释放最后在等待的A线程,防止死锁
            lock.unlock();
        });

        a.start();
        b.start();
    }

总结

如果想要指定执行顺序,则需要用信号量,或者自己实现一个标志位去模拟信号量

如果不需要指定执行顺序,则谁先竞争到锁就谁先执行,执行完后唤醒另一个线程,再进入wait状态

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

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

相关文章

深入学习指针5,与数组和指针相关的笔试题1(C语言)

前言 Hello,亲爱的小伙伴们&#xff0c;我又来了&#xff0c;&#xff0c;今天呢我们一起来学习一下C语言关于数组和指针的部分经典题目。如果觉得不错的话不要忘了点赞&#xff0c;收藏、关注&#xff0c;你的支持就是我更新的最大动力&#xff01;&#xff01; 好&#xff0…

什么是页分裂、页合并?

数据组织方式 在InnoDB存储引擎中&#xff0c;表数据都是根据主键顺序组织存放的&#xff0c;这种存储方式的表称为索引组织表(index organized table IOT)。 行数据&#xff0c;都是存储在聚集索引的叶子节点上的。而我们之前也讲解过InnoDB的逻辑结构图&#xff1a; 在I…

华为设备display查看命令

display version //查看版本信息 display current-configuration //查看配置详情 display this //查看当前视图有效配置 display ip routing-table //查看路由表 display ip routing-table 192.168.3.1 //查看去往3.1的路由 display ip interface brief //查看接口下ip信息 dis…

sudo apt-get update失败,怎么解决

本篇文章主要是从我的解决方案出发&#xff0c;因为个体差异性&#xff0c;对大家的帮助可能有限&#xff0c;不过大家也可以作为参考之一。 输入sudo apt-get update&#xff0c;结果一直显示&#xff1a; W: 无法下载 http://mirrors.aliyun.com/ubuntu/dists/jammy-securi…

利用一段代码轻松绕过PHP授权系统

第一步&#xff1a;首先你需要改名全局文件 比如说全局文件 common.php&#xff0c;那么 你将他改为core.php 第二步&#xff1a;创建文件 创建一个文件&#xff0c;和改名前的全局文件名称一样&#xff0c;然后把以下代码复制进去就OK了 代码如下&#xff1a; <?php…

20240513,常用算法(查找,排序,拷贝替换)

做着一些和考试无关的事情 常用查找算法——续 FIND_IF find_if //按条件查找元素&#xff0c;返回迭代器POS / END()find_if(beg,end,_Fred) _Fred函数或谓词&#xff08;返回BOOL类型的仿函数&#xff09; #include<iostream> #include<string> #includ…

【逆天OP懒狗的JAVA自学笔记--5.判断和循环】第二阶段始篇

文章目录 前言一、流程控制语句1.顺序结构&#xff08;最简单&#xff09;2.分支结构2.1 if 语句2.1.1 if语句的三种格式2.1.2 if 的注意事项 2.2 switch 语句2.2.1switch 的扩展知识 3.循环结构3.1 for 循环 扩展小点&#xff1a;//1.求和的变量不能定义在循环的里面&#xff…

韵搜坊(全栈)-- 前后端初始化

文章目录 前端初始化后端初始化 前端初始化 使用ant design of vue 组件库 官网快速上手&#xff1a;https://www.antdv.com/docs/vue/getting-started-cn 安装脚手架工具 进入cmd $ npm install -g vue/cli # OR $ yarn global add vue/cli创建一个项目 $ vue create ant…

WPF使用ItemsControl显示Object的所有属性值

对于上位机开发&#xff0c;我们有时候有这样的需求&#xff1a;如何显示所有的IO点位&#xff1f;比如有10个IO点位&#xff0c;那我们要写10个TextBlock去绑定这10个点位的属性&#xff08;本文暂时不考虑显示的样式&#xff0c;当然也可以考虑&#xff09;&#xff0c;当点位…

OBS插件--图片或视频源随着声音动态缩放

音效动态调整 应用此插件的源可以根据音效声音的高低进行动态的缩放。在本例中&#xff0c;我们在当前的场景里面添加了一个小喇叭的图片源&#xff0c;在这个图片源上引用这款滤镜插件&#xff0c;然后将VLC视频源的音效用于此插件的音效。设置完成后&#xff0c;场景中的小喇…

blender导出gltf模型混乱

最近用户给了几个blender文件&#xff0c;在blender打开是这样的&#xff1a; 我导出成gltf候&#xff0c;在本地打开时&#xff0c;底部发生了改变&#xff1a; 可以看出来&#xff0c;底部由原来的类型box变为了两个平面&#xff0c;后来我查了下blender里的属性设置&#xf…

三更草堂前后端分离个人博客项目的开发笔记

文章目录 项目实战-前后端分离博客系统1.课程介绍2.创建工程3.博客前台3.0 准备工作3.1 SpringBoot和MybatisPuls整合配置测试 3.1 热门文章列表3.1.0 文章表分析3.1.1 需求3.1.2 接口设计3.1.3 基础版本代码实现3.1.4 使用VO优化3.1.5 字面值处理 3.2 Bean拷贝工具类封装3.2 查…

flink优化案例

文章目录 一、flink join维表案例二、flink 双流join案例三、总结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考(适用于flink1.13) 一、flink join维表案例 背景:flink sql join 维表。job业务不复杂&#xff0c;job写入性能比较差。维表数据大约每天…

【ubuntu20.04运行python文件时,报错No module named ‘rospkg’】

**问题原因&#xff1a;**一般来说&#xff0c;并不是真的缺少rospkg&#xff0c;而是系统中存在多个python版本导致的混乱 检查python版本 Ubuntu20.04 —— pyhon3.8 sudo apt-get install python3.8最新版本&#xff0c;如下图所示 查看python3.8的位置 whereis python…

C++ | Leetcode C++题解之第88题合并两个有序数组

题目&#xff1a; 题解&#xff1a; class Solution { public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {int p1 m - 1, p2 n - 1;int tail m n - 1;int cur;while (p1 > 0 || p2 > 0) {if (p1 -1) {cur nums2[p2-…

让创意在幻觉中肆虐: 认识Illusion Diffusion AI

人工智能新境界 在不断发展的人工智能领域,一款非凡的新工具应运而生,它能将普通照片转化为绚丽的艺术品。敬请关注Illusion Diffusion,这是一个将现实与想象力完美融合的AI驱动平台,可创造出迷人的视错觉和超现实意境。 AI算法的魔力所在 Illusion Diffusion 的核心是借助先进…

每日OJ题_贪心算法四⑧_力扣767. 重构字符串

目录 力扣767. 重构字符串 解析代码 力扣767. 重构字符串 767. 重构字符串 难度 中等 给定一个字符串 s &#xff0c;检查是否能重新排布其中的字母&#xff0c;使得两相邻的字符不同。 返回 s 的任意可能的重新排列。若不可行&#xff0c;返回空字符串 "" 。 …

测试页打印失败。是否要参阅打印疑难解答以获得帮助?服务器打印后台处理程序服务没有运行。请重新启动服务器上的打印后台处理程序或重新启动服务器计算机。

问题&#xff1f; 测试页打印失败。是否要参阅打印疑难解答以获得帮助? 解决办法&#xff1a; 方法1、 请重新启动服务器上的打印后台处理程序或重新启动服务器计算机。 方法2、 找到services服务找到print spooler停止运行&#xff0c; C:\Windows\System32\spool -------…

回炉重造java----JVM

为什么要使用JVM ①一次编写&#xff0c;到处运行&#xff0c;jvm屏蔽字节码与底层的操作差异 ②自动内存管理&#xff0c;垃圾回收功能 ③数组下边越界检查 ④多态 JDK&#xff0c;JRE&#xff0c;JVM的关系 JVM组成部分 JVM的内存结构 《一》程序计数器(PC Register) 作用…

记录MySQL数据库查询不等于xxx时的坑

目录 一、背景 二、需求 三、方法 四、示例 一、背景 在使用MySQL数据库查询数据时&#xff0c;需要查询字段name不等于xxx的记录&#xff0c;通过where name ! xxx查询出来的记录不符合预期&#xff0c;通过检查发现少了name字段为null的记录&#xff0c;后经查询得知在My…