多线程顺序执行

前言

现在面试中,不光会问力扣之类的算法,手撕多线程问题也被提上了日程。多线程之间的顺序执行是一个高频的面试手撕题,而且在实际应用中也会有用武之地。因此在这里,我们考虑使用不同的方式来实现多线程的顺序执行。在本文中,我们采用了syncronize,ReentrantLock,join等方式来实现顺序执行。

方法实现

syncronize配合wait/notify

本方法采用sync提供的锁机制配合等待通知来实现多线程的顺序执行。具体代码如下:

public class ExcuteOrder {private final Object lock=new Object();private int current=1;public static void main(String[] args) {ExcuteOrder excuteOrder = new ExcuteOrder();excuteOrder.printOrder();}public void printOrder(){new Thread(()->printNums(3)).start();new Thread(()->printChars(2)).start();new Thread(()->printCH(1)).start();}public void printNums(int orderNum){synchronized (lock){while (orderNum!=current) {try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}for(int i=1;i<=3;i++){System.out.print(i);}current++;lock.notifyAll();}}public void printChars(int orderNum){synchronized (lock){while (orderNum!=current){try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}for(char i='A';i<='C';i++){System.out.print(i);}current++;lock.notifyAll();}}public void printCH(int orderNum){synchronized (lock){while (orderNum!=current){try {lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.print("你好");current++;lock.notifyAll();}}
}

此处设计的方法中传入一个执行编号,与成员变量进行比较,可以使得这几个线程以我们想执行的任意顺序来执行。

lock-condition配合await/signal

设计思路与上面类似,但是使用的是JDK提供的ReentrantLock,相较于sync,它能够提供更灵活的等待条件,并且它的等待通知机制更为灵活,从功能上也可以实现多线程的顺序执行。具体实现代码如下:

public class LockConditionFlow {private int currentNums=1;private ReentrantLock lock=new ReentrantLock();private Condition condition=lock.newCondition();public static void main(String[] args) {LockConditionFlow lockConditionFlow = new LockConditionFlow();lockConditionFlow.printFlow();}public void printFlow(){new Thread(()->printNums(1)).start();new Thread(()->printChars(2)).start();new Thread(()->printChinese(3)).start();}public void printNums(int excuNum){lock.lock();while (excuNum!=currentNums){try {condition.await();} catch (InterruptedException e) {throw new RuntimeException(e);}}for(int i=1;i<=3;i++){System.out.print(i);}currentNums++;condition.signalAll();lock.unlock();}public void printChars(int excuNums){lock.lock();while (excuNums!=currentNums){try {condition.await();} catch (InterruptedException e) {throw new RuntimeException(e);}}for(char i='A';i<='C';i++){System.out.print(i);}currentNums++;condition.signal();lock.unlock();}public void printChinese(int excuNums){lock.lock();while (excuNums!=currentNums){try {condition.await();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.print("你好");currentNums++;condition.signalAll();lock.unlock();}
}

join

join的使用相对较简单,它会阻塞调用此方法的线程,并等待上一个线程执行结束才能执行,具体实现代码如下:

public class PrintFlowJoin {public static void main(String[] args) {PrintFlowJoin printFlowJoin = new PrintFlowJoin();printFlowJoin.print();}public void print(){Thread thread1 = new Thread(this::printNums);Thread thread2 = new Thread(() -> {try {thread1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}printChars();});Thread thread3 = new Thread(() -> {try {thread2.join();} catch (InterruptedException e) {throw new RuntimeException(e);}printChinese();});thread1.start();thread2.start();thread3.start();}public void printNums(){for(int i=1;i<=3;i++){System.out.print(i);}}public void printChars(){for(char i='A';i<='C';i++){System.out.print(i);}}public void printChinese(){System.out.print("你好");}
}

CountDownLatch

它的思想在于等待countDownLatch的计数器置为零之后唤醒等待在该countDownLatch上的线程,比较适用于多个线程等待某个线程执行完毕之后再执行,类似于一种并行的思想,当然也可以用来实现多个线程的顺序执行,具体代码如下:

public class ExcuteCountDown {public static void main(String[] args) {ExcuteCountDown excuteCountDown = new ExcuteCountDown();excuteCountDown.excute();}public void excute(){CountDownLatch countDownLatch1 = new CountDownLatch(1);CountDownLatch countDownLatch2 = new CountDownLatch(1);Thread thread1 = new Thread(() -> {countDownLatch1.countDown();System.out.println("Thread1");});Thread thread2 = new Thread(() -> {try {countDownLatch1.await();System.out.println("Thread2");countDownLatch2.countDown();} catch (InterruptedException e) {throw new RuntimeException(e);}});Thread thread3 = new Thread(() -> {try {countDownLatch2.await();System.out.println("Thread3");} catch (InterruptedException e) {throw new RuntimeException(e);}});thread1.start();thread2.start();thread3.start();}
}

总结

这几种方案都可以来实现线程的顺序执行,但是它们也有一些区别。sync的方法实现起来比较简单,但是需要手动等待和通知容易造成死锁;而lock-condition的方式提供了更为灵活的等待通知机制,并且可以提供多个condition,所以灵活性很高,但是需要对lock-condition的使用很熟徐;join方法使用也非常简单,只能实现一些简单场景的线程顺序执行;最后是CountDownLatch,它比较适合更为复杂的线程协作场景。

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

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

相关文章

Jackson 库简介--以及数据脱敏

Jackson 是一个流行的 Java JSON 处理库&#xff0c;它提供了将 Java 对象与 JSON 数据相互转换的功能。Jackson 的主要功能包括&#xff1a; 序列化&#xff1a;将 Java 对象转换为 JSON 字符串。反序列化&#xff1a;将 JSON 字符串转换为 Java 对象。 Jackson 提供了以下几…

C2W2.Assignment.Parts-of-Speech Tagging (POS).Part2

理论课&#xff1a;C2W2.Part-of-Speech (POS) Tagging and Hidden Markov Models 文章目录 2 Hidden Markov Models2.1 Generating MatricesCreating the A transition probabilities matrixExercise 03Create the B emission probabilities matrixExercise 04 理论课&#x…

FastAPI 学习之路(五十六)将token缓存到redis

在之前的文章中&#xff0c;FastAPI 学习之路&#xff08;二十九&#xff09;使用&#xff08;哈希&#xff09;密码和 JWT Bearer 令牌的 OAuth2&#xff0c;FastAPI 学习之路&#xff08;二十八&#xff09;使用密码和 Bearer 的简单 OAuth2&#xff0c;FastAPI 学习之路&…

Kubernetes 之 Ingress

Kubernetes 之 Ingress 定义 Ingress 可以把外部需要进入到集群内部的请求转发到集群中的一些服务上&#xff0c;从而实现把服务映射到集群外部的需要。Ingress 能把集群内 Service 配置成外网能够访问的 URL&#xff0c;流量负载均衡&#xff0c;提供基于域名访问的虚拟主机…

RabbitMQ 和 RocketMQ 的区别

RabbitMQ 和 RocketMQ 都是流行的开源消息中间件&#xff0c;它们用于在分布式系统中异步传输消息。尽管它们都实现了核心的消息队列功能&#xff0c;但它们在设计、性能、特性和使用场景上有一些关键的区别&#xff1a; 基础架构: RabbitMQ: 基于AMQP&#xff08;高级消息队列…

阵列信号处理学习笔记(二)--空域滤波基本原理

阵列信号 阵列信号处理学习笔记&#xff08;一&#xff09;–阵列信号处理定义 阵列信号处理学习笔记&#xff08;二&#xff09;–空域滤波基本原理 文章目录 阵列信号前言一、阵列信号模型1.1 信号的基本模型1.2 阵列的几何构型1.3 均匀直线阵的阵列信号基本模型 总结 前言…

HOW - React 处理不紧急的更新和渲染

目录 useDeferredValueuseTransitionuseIdleCallback 在 React 中&#xff0c;有一些钩子函数可以帮助你处理不紧急的更新或渲染&#xff0c;从而优化性能和用户体验。 以下是一些常用的相关钩子及其应用场景&#xff1a; useDeferredValue 用途&#xff1a;用于处理高优先级…

嵌入式面试总结

C语言中struct和union的区别 struct和union都是常见的复合结构。 结构体和联合体虽然都是由多个不同的数据类型成员组成的&#xff0c;但不同之处在于联合体中所有成员共用一块地址空间&#xff0c;即联合体只存放了一个被选中的成员&#xff0c;结构体中所有成员占用空间是累…

【网络】windows和linux互通收发

windows和linux互通收发 一、windows的udp客户端代码1、代码剖析2、总体代码 二、linux服务器代码三、成果展示 一、windows的udp客户端代码 1、代码剖析 首先我们需要包含头文件以及lib的一个库&#xff1a; #include <iostream> #include <WinSock2.h> #inclu…

前端页面是如何禁止被查看源码、被下载,被爬取,以及破解方法

文章目录 1.了解禁止查看,爬取原理1.1.JS代码,屏蔽屏蔽键盘和鼠标右键1.2.查看源码时,通过JS控制浏览器窗口变化2.百度文库是如何防止抓包2.1.HTPPS2.2. 动态加载为什么看不到?如何查看动态加载的内容?3.禁止复制,如果解决3.1.禁止复制原理3.2.如何破解1.了解禁止查看,爬…

使用scikit-learn进行机器学习:基础教程

使用scikit-learn进行机器学习&#xff1a;基础教程 Scikit-learn是Python中最流行的机器学习库之一。它提供了简单易用的工具&#xff0c;帮助我们进行数据预处理、模型训练、评估和预测。本文将带你通过一个基础教程&#xff0c;了解如何使用scikit-learn进行机器学习。 1.…

【模板代码】用于编写Threejs Demo的模板代码

基础模板代码 使用须知常规模板代码常规Shader模板代码 使用须知 本模板代码&#xff0c;主要用于编写Threejs的Demo&#xff0c;因为本人在早期学习的过程中&#xff0c;大量抄写Threejs/examples下的代码以及各个demo站的代码&#xff0c;所以养成了编写Threejs的demo的习惯…

SAP 采购订单 Adobe 消息输出

目录 1 简介 2 业务数据例子 3 选择增强 & 代码 1&#xff09;BADI: MM_PUR_S4_PO_MODIFY_HEADER 2&#xff09;BADI: MM_PUR_S4_PO_MODIFY_ITEM 4 自定义 Adobe form 1&#xff09;PO Master form 2&#xff09;PO form 5 前台主数据配置 6 后台配置 1&#xf…

昇思22天

CycleGAN图像风格迁移互换 CycleGAN&#xff08;循环生成对抗网络&#xff09;是一种用于在没有成对训练数据的情况下学习将图像从源域 X 转换到目标域 Y 的方法。该技术的一个重要应用是域迁移&#xff0c;即图像风格迁移。 模型介绍 模型简介: CycleGAN 来自于论文 Unpair…

掌握Rust:函数、闭包与迭代器的综合运用

掌握Rust&#xff1a;函数、闭包与迭代器的综合运用 引言&#xff1a;解锁 Rust 高效编程的钥匙函数定义与模式匹配&#xff1a;构建逻辑的基石高阶函数与闭包&#xff1a;代码复用的艺术迭代器与 for 循环&#xff1a;高效数据处理的引擎综合应用案例&#xff1a;构建一个简易…

Mybatis——一对多处理

环境搭建 与多对一相似&#xff0c;有一些地方需要改动&#xff1a; 实体类&#xff1a; Data public class Student {private int id;private String name;private int tid;} Data public class Teacher {private int id;private String name;// 一个老师拥有多个学生priv…

【LeetCode】day15:110 - 平衡二叉树, 257 - 二叉树的所有路径, 404 - 左叶子之和, 222 - 完全二叉树的节点个数

LeetCode 代码随想录跟练 Day15 110.平衡二叉树257.二叉树的所有路径404.左叶子之和222.完全二叉树的节点个数 110.平衡二叉树 题目描述&#xff1a; 给定一个二叉树&#xff0c;判断它是否是 平衡二叉树 平衡二叉树的定义是&#xff0c;对于树中的每个节点&#xff0c;其左右…

qt自定义控件(QLabel)

先创建自定义控件类painter_label 1.自定义类必须给基类传入父窗口指针 2.重写控件中的方法 3.在UI中创建一个QLabel,右键“提升为”&#xff0c;输入类名

动画革命:Lottie如何改变我们对移动应用交互的认知

在数字世界的浩瀚星空中&#xff0c;每一个像素都跃动着无限创意与想象的火花。当静态的界面遇上动态的魔法&#xff0c;一场视觉盛宴便悄然开启。今天&#xff0c;让我们一同揭开一位幕后英雄的神秘面纱——Lottie&#xff0c;这个在UI/UX设计界掀起波澜的动画利器&#xff0c…

掌握构建自动化:如何在Gradle中使用Init脚本进行构建初始化

掌握构建自动化&#xff1a;如何在Gradle中使用Init脚本进行构建初始化 在现代软件开发中&#xff0c;自动化构建是提高效率和一致性的关键。Gradle&#xff0c;作为一个功能强大的构建工具&#xff0c;提供了丰富的自动化支持。其中&#xff0c;Init脚本是Gradle中用于初始化…