当前位置: 首页 > news >正文

让线程按指定顺序运行

1 概述

郝林老师在《Go 语言核心》课程里提了个有意思的问题:当多个线程运行之后,如何能够让线程按指定的顺序执行?多个线程按顺序执行的效果和串行是一样的,根本没有必要用多线程。但细想一下,其实这里面应该包含着线程调度的知识,比如当一件比较耗时的事情可以分成几个子任务,有些任务可以并行执行,而整体又有一定的串行执行要求,此时就可以用上按一定顺序调度某些线程的方法。

这里有两个要求:一是线程是运行起来了的,不是一个执行完之后再启动另外一个;二是调度的代码尽可能要剥离到业务逻辑代码之外。

2 实现方法

2.1 Go版本

郝林老师给了个Go实现的版本,Go里面不叫线程而是Go程,先给没有顺序要求的版本,满足要求一:

for i := 0; i < 10; i++ {go func(i int) {fmt.Println(i)}(i)
}

在此基础上封装一下,封装的代码不影响原来主体业务逻辑代码,满足要求二:

var count uint32 = 0 trigger := func(i uint32, fn func()) {for {if n := atomic.LoadUint32(&count); n == i {fn()atomic.AddUint32(&count, 1)break}time.Sleep(time.Nanosecond)}
}for i := uint32(0); i < 10; i++ {go func(i uint32) {fn := func() {fmt.Println(i)}trigger(i, fn)}(i)
}

2.2 Java版

不协调线程顺序:

public class ThreadSeqOrderDemo {public static void main(String[] args) {ThreadSeqOrderDemo demo = new ThreadSeqOrderDemo();demo.test();}private void test() {int count = 10;Thread[] threads = new Thread[count];for (int i = 0; i < count; i++) {threads[i] = new Thread(new TestTask(i + 1));threads[i].start();}for (int i = 0; i < count; i++) {try {threads[i].join();} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class TestTask implements Runnable {private int i;public TestTask(int i) {this.i = i;}@Overridepublic void run() {System.out.println(i);}public int getIndex() {return i;}
}

增加一个Wrapper,相当于增加一个切面,把调度封装起来:

public class ThreadSeqOrderDemo {public static void main(String[] args) {ThreadSeqOrderDemo demo = new ThreadSeqOrderDemo();demo.test();}private void test() {int count = 10;AtomicInteger counter = new AtomicInteger(1); // 增加协调用的countThread[] threads = new Thread[count];for (int i = 0; i < count; i++) {// 用Wrapper把Task包装起来threads[i] = new Thread(new TestTaskWrapper(new TestTask(i + 1), counter));threads[i].start();}for (int i = 0; i < count; i++) {try {threads[i].join();} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class TestTask implements Runnable {private int i;public TestTask(int i) {this.i = i;}@Overridepublic void run() {System.out.println(i);}public int getIndex() {return i;}
}
public class TestTaskWrapper implements Runnable {private TestTask task;private AtomicInteger counter;public TestTaskWrapper(TestTask task, AtomicInteger counter) {this.task = task;this.counter = counter;}@Overridepublic void run() {while (true) {// counter值和任务编号一样时才执行if(counter.get() == task.getIndex()) {task.run();// 触发下一个要执行的编号counter.incrementAndGet();break;}try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}}
}

2.3 CountDownLatch版本

上面版本用一个while(true)的方式,不够优雅,可以改用CountDownLatch改造一下:

public class ThreadSeqOrderDemo {public static void main(String[] args) {ThreadSeqOrderDemo demo = new ThreadSeqOrderDemo();demo.test();}private void test() {int count = 10;Thread[] threads = new Thread[count];CountDownLatch[] latches = new CountDownLatch[count]; // 数字换成CountDownLatchCountDownLatch preLatch = null;for (int i = 0; i < count; i++) {// 按要求把协调用的CountDownLatch和Task组装到Wrapper里threads[i] = new Thread(new TestTaskWrapper2(new TestTask(i + 1), preLatch, latches[i]));threads[i].start();preLatch = latches[i];}for (int i = 0; i < count; i++) {try {threads[i].join();} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class TestTask implements Runnable {private int i;public TestTask(int i) {this.i = i;}@Overridepublic void run() {System.out.println(i);}public int getIndex() {return i;}
}
public class TestTaskWrapper2 implements Runnable {private TestTask task;/** 上一个Latch */private CountDownLatch preLatch;/** 当前Latch */private CountDownLatch curLatch;public TestTaskWrapper2(TestTask task, CountDownLatch preLatch, CountDownLatch curLatch) {this.task = task;this.preLatch = preLatch;this.curLatch = curLatch;}@Overridepublic void run() {// 第一个任务没有preLatch,可以直接执行,有preLatch则等待其执行完的通知if(preLatch != null) {try {preLatch.await();} catch (InterruptedException e) {e.printStackTrace();}}task.run();// 触发当前Latch完成,通知下一个任务执行curLatch.countDown();}
}

换成CountDownLatch就有点协调的感觉了,不再依赖固定的数字,而是抽象成一系列协调对象,当希望线程按指定顺序执行时,调整这些协调对象并与对应的线程和任务绑定即可,其核心原理就是执行完一部分再通知下一部分执行,在没有上一部分完成的通知之前,线程处于等待状态。

http://www.dtcms.com/a/520540.html

相关文章:

  • 定制手机网站开发显示网站翻页代码
  • CoAlbum:多级缓存与性能对比
  • 【算法专题训练】27、树的层序遍历
  • 网站导入wordpress基层建设是哪个网站的
  • 第6章—手动移植创建STM32工程
  • Android Bluetooth 蓝牙通信
  • 简述一般网站开发方式广州三合一企业网站哪家好
  • 网站建设邀标函专业的外贸建站公司
  • C++ STL(标准模板库)深度解析:从基础到实践
  • 压缩与缓存调优实战指南:从0到1根治性能瓶颈(二)
  • Linux小课堂: SSH 配置文件详解之全局与局部 Config 文件的语义梳理与技术深化
  • 6-2〔O҉S҉C҉P҉ ◈ 研记〕❘ 客户端攻击▸利用WORD宏让客户端执行命令
  • 网站页面设计报价xampp做网站设置
  • 制作 网站 盈利怎么看网站到期时间
  • Qt6.10 | Qt Bluetooth 蓝牙
  • 网站自动化开发无极官方网站下载
  • 基于 docker compose 进行部署PandaWiki
  • 哪里建设网站设计服务
  • Python - 100天从新手到大师:第五十八天 Python中的并发编程(1-3)
  • C语言-动态内存分配
  • 多个PDF文档如何批量删除页眉处的多余信息
  • 网站服务器空间大小网站自适应宽度
  • 静态网站什么样做个简单的网站
  • EtherCAT转EtherNet/IP工业PLC网关:实现PLC与底层设备的无缝协同控制
  • 群晖边缘存储方案,让数据更近、更快、更安全
  • Python电力负荷预测:LSTM、GRU、DeepAR、XGBoost、Stacking、ARIMA结合多源数据融合与SHAP可解释性的研究
  • 做网站送的小程序有什么用多多进宝怎么推广赚钱
  • 做彩票类网站用什么服务器图片生成二维码软件
  • 机器学习(7)逻辑回归及其成本函数
  • 计算机视觉六大前沿创新方向