java并发-如何保证线程按照顺序执行?

【readme】

  1. 使用只有单个线程的线程池(最简单
  2. Thread.join() 
  3. 可重入锁 ReentrantLock + Condition 条件变量(多个) ; 原理如下:
    1. 任务1执行前在锁1上阻塞;执行完成后在锁2上唤醒;
    2. 任务2执行前在锁2上阻塞,执行完成后在锁3上唤醒;
    3. 任务n执行前在锁n上阻塞,执行完成后在锁n+1上唤醒;
    4. 以此类推 ..............
    5. 补充:
      1. 第1条任务执行前可以不阻塞,但执行完成后必须唤醒;(如果要阻塞,则可以让主线程来唤醒第1条任务);
      2. 补充: 最后一条任务执行后可以不唤醒,但执行前必须阻塞; (如果要唤醒,则最后一条任务执行完成后唤醒主线程)
  4. 与可重入锁类似,可以使用monitor监视器锁(多个);
  5. 与可重入锁类似,使用 Semaphore 信号量(多个);
  6. 与可重入锁类似,CountDownLatch : 倒计时锁存器(多个); 
  7. 与可重入锁类似,CyclicBarrier 循环栅栏(多个) ;

【1】单个线程的线程池

参数设置:核心线程数=1, 最大线程数=1,就能保证线程池中只有1个线程在运行;

public class OrderlySingleThreadPoolTest {public static void main(String[] args) {ThreadPoolExecutor singleThreadPool =new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));singleThreadPool.execute(new Task(1));singleThreadPool.execute(new Task(2));singleThreadPool.execute(new Task(3));singleThreadPool.execute(new Task(4));singleThreadPool.execute(new Task(5));singleThreadPool.shutdown();}private static class Task implements Runnable {int order; // 执行序号Task(int order) {this.order = order;}@Overridepublic void run() {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}PrintUtils.print("序号=" + order + "执行完成");}}
}

【打印结果】

2024-06-09 16:07:59.398 序号=1执行完成
2024-06-09 16:08:02.404 序号=2执行完成
2024-06-09 16:08:05.411 序号=3执行完成
2024-06-09 16:08:08.425 序号=4执行完成
2024-06-09 16:08:11.439 序号=5执行完成

【2】thread.join()

main 调用 t1.join(),则main线程阻塞直到t1线程执行完成;如下。

public class ThreadJoinTest {public static void main(String[] args) {f1();PrintUtils.print("主线程结束");}public static void f1() {Thread t1 = new Thread(()->{try {TimeUnit.SECONDS.sleep(5);PrintUtils.print("t1线程结束");} catch (InterruptedException e) {throw new RuntimeException(e);}});t1.start();try {t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
2024-06-09 07:44:38.474 t1线程结束
2024-06-09 07:44:38.573 主线程结束

【3】可重入锁+条件变量实现多个线程顺序执行

1. 补充:

condition.await() 调用前需要获取锁;调用后释放锁(其他线程可以获取该锁,因此得名为可重入),但当前线程阻塞

condition.signal() 调用前需要获取锁;调用后释放锁;

public class OrderlyReentrantLockTest {private static Condition[][] build(ReentrantLock reentrantLock, int num) {Condition[][] arr = new Condition[num][2];arr[0] = new Condition[]{null, reentrantLock.newCondition()};int i = 1;for (; i < num - 1; i++) {arr[i] = new Condition[]{arr[i - 1][1], reentrantLock.newCondition()};}arr[i] = new Condition[]{arr[i - 1][1], null};return arr;}public static void main(String[] args) {int threadNum = 5;ThreadPoolExecutor threadPool =new ThreadPoolExecutor(threadNum, threadNum, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));ReentrantLock reentrantLock = new ReentrantLock(true);AtomicInteger unWaitNum = new AtomicInteger(threadNum);// 构建条件变量数组Condition[][] conditionTwoArr = build(reentrantLock, threadNum);// 提交任务for (int order = threadNum; order >= 1; order--) {OrderlyTask orderlyTask = new OrderlyTask(reentrantLock, conditionTwoArr[order - 1], order, unWaitNum);threadPool.execute(orderlyTask);// 阻塞成功,才提交下一个任务while (unWaitNum.get() == threadNum) ; // 这里可能死循环,但可以新增超时重试机制来处理PrintUtils.print("阻塞成功,线程order=" + order);} threadPool.shutdown();}private static class OrderlyTask implements Runnable {private ReentrantLock lock;private Condition[] conditions;private int order; // 执行序号private AtomicInteger unWaitNum;OrderlyTask(ReentrantLock reentrantLock, Condition[] conditions, int order, AtomicInteger unWaitNum) {this.lock = reentrantLock;this.conditions = conditions;this.order = order;this.unWaitNum = unWaitNum;}@Overridepublic void run() {lock.lock();try {unWaitNum.decrementAndGet();try {if (conditions[0] != null) {conditions[0].await(); // 在第1个条件变量上阻塞}} catch (Exception e) {unWaitNum.incrementAndGet();throw e;}// 处理业务逻辑TimeUnit.SECONDS.sleep(3);// 唤醒在第2个条件变量上阻塞的线程if (conditions[1] != null) {conditions[1].signal();}} catch (Exception e) {System.err.println(e);} finally {lock.unlock();}PrintUtils.print("执行完成, 线程order=" + order + ", 线程id=" + Thread.currentThread().getName());}}
}

打印结果:

2024-06-09 22:16:00.696 阻塞成功,线程order=5
2024-06-09 22:16:00.698 阻塞成功,线程order=4
2024-06-09 22:16:00.698 阻塞成功,线程order=3
2024-06-09 22:16:00.698 阻塞成功,线程order=2
2024-06-09 22:16:00.698 阻塞成功,线程order=1
2024-06-09 22:16:03.707 执行完成, 线程order=1, 线程id=pool-1-thread-5
2024-06-09 22:16:06.719 执行完成, 线程order=2, 线程id=pool-1-thread-4
2024-06-09 22:16:09.719 执行完成, 线程order=3, 线程id=pool-1-thread-3
2024-06-09 22:16:12.727 执行完成, 线程order=4, 线程id=pool-1-thread-2
2024-06-09 22:16:15.729 执行完成, 线程order=5, 线程id=pool-1-thread-1

【4】使用CountDownLatch倒计时锁存器 

public class OrderlyCountDownLatchTest {private static CountDownLatch[][] build(int num) {CountDownLatch[][] arr = new CountDownLatch[num][2];arr[0] = new CountDownLatch[]{null, new CountDownLatch(1)};int i = 1;for (; i < num - 1; i++) {arr[i] = new CountDownLatch[]{arr[i - 1][1], new CountDownLatch(1)};}arr[i] = new CountDownLatch[]{arr[i - 1][1], null};return arr;}public static void main(String[] args) {int threadNum = 5;ThreadPoolExecutor threadPool =new ThreadPoolExecutor(threadNum, threadNum, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));AtomicInteger unWaitNum = new AtomicInteger(threadNum);// 构建倒计时锁存器数组CountDownLatch[][] latchArr = build(threadNum);// 提交任务for (int order = threadNum; order >= 1; order--) {OrderlyTask orderlyTask = new OrderlyTask(latchArr[order - 1], order, unWaitNum);threadPool.execute(orderlyTask);// 阻塞成功,才提交下一个任务while (unWaitNum.get() == threadNum) ; // 这里可能死循环,但可以新增超时重试机制来处理PrintUtils.print("阻塞成功,线程order=" + order);}threadPool.shutdown();}private static class OrderlyTask implements Runnable {private CountDownLatch[] latchArr;private int order; // 执行序号private AtomicInteger unWaitNum;OrderlyTask(CountDownLatch[] latchArr, int order, AtomicInteger unWaitNum) {this.latchArr = latchArr;this.order = order;this.unWaitNum = unWaitNum;}@Overridepublic void run() {try {unWaitNum.decrementAndGet();try {if (latchArr[0] != null) {latchArr[0].await(); // 在第1个锁存器上阻塞}} catch (Exception e) {unWaitNum.incrementAndGet();throw e;}// 处理业务逻辑TimeUnit.SECONDS.sleep(3);// 唤醒在第2个条件变量上阻塞的线程if (latchArr[1] != null) {latchArr[1].countDown();}} catch (Exception e) {System.err.println(e);}PrintUtils.print("执行完成, 线程order=" + order + ", 线程id=" + Thread.currentThread().getName());}}
}

打印结果: 
 

2024-06-09 22:35:13.648 阻塞成功,线程order=5
2024-06-09 22:35:13.651 阻塞成功,线程order=4
2024-06-09 22:35:13.651 阻塞成功,线程order=3
2024-06-09 22:35:13.651 阻塞成功,线程order=2
2024-06-09 22:35:13.651 阻塞成功,线程order=1
2024-06-09 22:35:16.664 执行完成, 线程order=1, 线程id=pool-1-thread-5
2024-06-09 22:35:19.676 执行完成, 线程order=2, 线程id=pool-1-thread-4
2024-06-09 22:35:22.682 执行完成, 线程order=3, 线程id=pool-1-thread-3
2024-06-09 22:35:25.684 执行完成, 线程order=4, 线程id=pool-1-thread-2
2024-06-09 22:35:28.688 执行完成, 线程order=5, 线程id=pool-1-thread-1

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

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

相关文章

【算法篇】大数加法JavaScript版

题目描述 以字符串的形式读入两个数字&#xff0c;编写一个函数计算它们的和&#xff0c;以字符串形式返回。 数据范围&#xff1a;s.length,t.length≤100000&#xff0c;字符串仅由’0’~‘9’构成 要求&#xff1a;时间复杂度 &#x1d442;(&#x1d45b;) 示例1 输入&…

BGP的属性1

BGP的属性——基础属性 传播范围 默认值 大/小 优 1、PrefVal(优先级) 不传播 0-65535 大 该属性为私有属性(华为) [r3]bgp 2 [r3-…

Java Web学习笔记29——Vue路由

Vue路由&#xff1a; 前端路由&#xff1a;点击菜单栏&#xff0c;地址栏会发生变化&#xff0c;会显示对应的组件。 URL中的Hash&#xff08;#号后面的部分&#xff09;与组件之间的对应关系。 Hash是/dept&#xff0c;那么就是部门管理组件&#xff1b; Hash是/emp, 那么…

Macbook M芯片Maven的安装与配置

Macbook M芯片Maven的安装与配置 下载 搜索Maven 进入网站 https://maven.apache.org/download.cgi 点击Download 点击如下链接进行下载&#xff1b; 将下载好的文件放到你的指定位置 双击进行解压 配置环境变量 进入终端 在终端中输入 open ~/.bash_profile输入以下内…

Zynq7000 系列FPGA模块化仪器

• 基于 XilinxXC7Z020 / 010 / 007S • 灵活的模块组合 • 易于嵌入的紧凑型外观结构 • 高性能的 ARM Cortex 处理器 • 成熟的 FPGA 可编程逻辑 &#xff0c;基于 IP 核的软件库 FPGA 控制器 Zynq7000 系列模块是基于 Xilinx XC7Z020/010/007S 全可编程片上系统 (SoC) 的…

湖南(品牌控价)源点调研 手机价格管理对品牌的影响分析

前言&#xff1a;手机自发明以来&#xff0c;过去一直是国际品牌占主导地位&#xff0c;从最初的爱立信、摩托罗拉&#xff0c;到后来的诺基亚、三星&#xff0c;苹果在这个手机行业里&#xff0c;竞争激励&#xff0c;没有百年企业&#xff0c;每个品牌的盛衰都有背后的历史背…

View->View测量布局中requestLayout和forceLayout的区别

XML文件 <?xml version"1.0" encoding"utf-8"?> <com.gallery20.app.MyLinearLayoutxmlns:android"http://schemas.android.com/apk/res/android"android:id"id/my_ll"android:layout_width"match_parent"andro…

可选链操作符

<span class"username">{{ username?.charAt(0) }}</span>这里我们使用了可选链操作符 &#xff1f;&#xff0c; 它的意思是当&#xff1f;前面的变量为空时&#xff0c;它不会继续往下执行&#xff0c;防止报错&#xff0c;如 null?.name&#xff0c…

手写kNN算法的实现-用余弦相似度来度量距离

设a为预测点&#xff0c;b为其中一个样本点&#xff0c;在向量空间里&#xff0c;它们的形成的夹角为θ&#xff0c;那么θ越小&#xff08;cosθ的值越接近1&#xff09;&#xff0c;就说明a点越接近b点。所以我们可以通过考察余弦相似度来预测a点的类型。 from collections i…

Linux Ext2/3/4文件系统

文章目录 前言一、Linux文件系统简介1.1 简介1.2 Linux File System Structure1.3 Directory Structure 二、Ext2/3/4文件系统2.1 Minix2.2 EXT2.3 EXT22.4 EXT32.5 EXT4 三、EXT Inode参考资料 前言 这篇文章介绍了Linux文件系统的一些基础知识&#xff1a;Linux 文件系统简介…

vs - vs2013中编译sqlite3.44.2

文章目录 vs - vs2013中编译sqlite3.44.2概述笔记工程输出归档END vs - vs2013中编译sqlite3.44.2 概述 以前在vs2019下编译了sqlite3.44.2, 好使。做了笔记(sqlite3.44.2的编译) 现在准备将手头的vs2019工程改为vs2013的&#xff0c;自然要将sqlite也编译为vs2013版本的。 按…

Javaweb02-XML概述

第一章 XML概述 1.XML基本概念 什么是xml&#xff1f; **a.**引入的原因&#xff1a;为了解决不同不同语言之间的数据传输的格式不同 **b.**概念&#xff1a;XML是一种可扩展标记语言&#xff0c;适用于不同数据之间的数据交换 **c.**XML文档&#xff1a;通过元素的嵌套&a…

[HNCTF 2022 WEEK4]flower plus

第一种花指令 第二种花指令 根据两种花指令特征&#xff0c;写出去花指令脚本 saddr0x401000 eaddr0x435000 for i in range(saddr,eaddr):if get_wide_dword(i)0x01740275:print(hex(i),hex(get_wide_dword(i)))patch_byte(i-5,0x90)patch_dword(i-4,0x90909090)patch_dw…

插卡式仪器模块:数字万用表模块(插卡式)

• 6 位数字表显示 • 24 位分辨率 • 250 KSPS 采样率 • 电源和数字 I/O 均采用隔离抗噪技术 • 电压、电流、电阻、电感、电容的高精度测量 • 二极管/三极管测试 通道122输入 阻抗 电压10 MΩHigh-Z, 10 MΩ电流10 Ω50 mΩ / 2 Ω / 2 KΩ输入范围电压 5 V0–60 V电流…

Ubuntu 配置动态链接器的搜索路径(/etc/ld.so.conf.d)

引言 为了使程序在运行时可以正常找到指定的lib库&#xff0c;防止出现卡死或闪退现象。 /etc/ld.so.conf.d 路径是程序运行时&#xff0c;系统查找共享库&#xff08;如 .so 文件&#xff09;的位置。 简要说明 如果你安装了一个第三方软件&#xff0c;它可能将其共享库放在…

【C语言】Leetcode-312 戳气球

文章目录 题目思路代码如下 题目 链接: Leetcode-312 戳气球 思路 我们观察戳气球的操作&#xff0c;发现这会导致两个气球从不相邻变成相邻&#xff0c;使得后续操作难以处理。于是我们倒过来看这些操作&#xff0c;将全过程看作是每次添加一个气球。 首先 我们需要创建一个…

【xilinx】使用vivado编译中methodology的相关介绍

Vivado Methodology 是 Xilinx 提供的一系列设计流程和最佳实践&#xff0c;旨在帮助工程师使用 Vivado Design Suite 工具链来开发高质量的 FPGA 和 SoC 应用。Vivado Methodology 涵盖了从设计开始到最终实现的整个流程&#xff0c;包括设计规划、实现、验证和优化等多个方面…

EF Core Model-First

使用C# .NET Core 3.1和EF Core Model-First方法生成数据库表的详细步骤&#xff0c;并介绍EF Core数据库迁移常用命令。 使用C# .NET Core 3.1和EF Core的Model-First方法生成数据库表 1. 安装EF Core包 首先&#xff0c;在你的项目中安装EF Core和相关的工具包。可以使用Nu…

后端Long类型参数前端接受精度丢失解决方案

背景 在项目中使用雪花Id作为主键Id使用&#xff0c;前端返回查询数据时在展示时出现精度丢失&#xff0c;如原Id为1797913405167583236进度丢失后为1797913405167583000 解决方案&#xff08;前后端&#xff09; 前端方案 前端解决精度问题很简单&#xff0c;通过字符串接…

58.CountdownLatch

用来进行线程同步协作,等待所有线程完成倒计时。 构造参数用来初始化等待计数值,await方法用来等待计数归零,countDown方法用来让计数减一。 CountdownLatch普通使用 @Slf4j public class CountdownLatchDemo {public static void main(String[] args) {CountDownLatch c…