Java并发编程(二)如何保证线程同时/交替执行
第一篇文章中,我用如何保证线程顺序执行的例子作为Java并发系列的开胃菜。本篇我们依然不会有源码分析,而是用另外两个多线程的例子来引出Java.util.concurrent中的几个并发工具的用法。
系列文章
Java并发编程(一)如何保证线程顺序执行 - 简书 (jianshu.com)
一、如何保证多个线程同时执行
保证多个线程同时执行,指的是多个线程在同一时间开始执行内部run()方法。
经过第一篇的学习,你应该能理解到,让线程能按我们的意志来运行其实是需要用一些手段(信号量、并发工具、线程池等)来实现的。常用的并发工具一般有CountDownLatch、CyclicBarrier、Semaphore,这些工具在多线程编程中必不可少。我们先看看如何用并发工具保证线程同时执行吧。
1. 使用CountDownLatch实现
关于CountDownLatch,count down的字面意思是倒数,latch是上锁的意思。所以CountDownLatch的意思就是倒数关门。我们看看JDK8 API中是如何解释的:
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
大概意思是,CountDownLatch是一种同步辅助工具,允许一个或多个线程等待一组在其他线程中执行的操作完成之后再执行。
public class SimultaneouslyExample {
static CountDownLatch countDownLatch=new CountDownLatch(3);
public static void foo(String name) {
System.out.println("线程名:"+name+",开始时间:"+System.nanoTime());
try {
countDownLatch.await();
//2.每次减一
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException{
Thread thread1 = new Thread(() -> foo("A"));
Thread thread2 = new Thread(() -> foo("B"));
Thread thread3 = new Thread(() -> foo("C"));
thread1.start();
thread2.start();
thread3.start();
Thread.sleep(300);
countDownLatch.countDown();
}
}
输出结果:
线程名:A,开始时间:449768159780400
线程名:C,开始时间:449768159785200
线程名:B,开始时间:449768159795300
看到输出结果,你可能会怀疑。明明A线程慢了4800纳秒啊,这不是同步的。其实大可不必觉得奇怪,纳秒级的时间即使是JVM也没办法那么精准的把控,不过根据我的测试。这里的同步实现逻辑能保证毫秒级的精确性。
2. 使用CyclicBarrier实现
另一种实现方式CyclicBarrier,根据字面意思我可以看到这个是一个可循环屏障。CyclicBarrier可以让一个或多个线程到达一个屏障点之后再开始运行。
话不多说,我们直接看看代码中如何写:
public class CyclicBarrierExample{
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
public static void foo(String name) {
System.out.println("线程名:"+name+",开始时间:"+System.currentTimeMillis());
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException{
Thread thread1 = new Thread(() -> foo("A"));
Thread thread2 = new Thread(() -> foo("B"));
Thread thread3 = new Thread(() -> foo("C"));
thread1.start();
thread2.start();
thread3.start();
Thread.sleep(300);
}
}
输出结果:
线程名:A,开始时间:1621232496385
线程名:B,开始时间:1621232496385
线程名:C,开始时间:1621232496385
二、如何保证多个线程交替执行
保证多个线程交替执行,指的是多个线程可以按照一定的次序开始执行内部run()方法。这里我们需要使用Semaphore并发工具来实现。如何你的大学课程学习过操作系统的话,那么你一定对信号量机制很熟悉
Semaphore(信号量):是一种计数器,用来保护一个或者多个共享资源的访问。如果线程要访问一个资源就必须先获得信号量。如果信号量内部计数器大于0,信号量减1,然后允许共享这个资源;否则,如果信号量的计数器等于0,信号量将会把线程置入休眠直至计数器大于0.当信号量使用完时,必须释放。
Semaphore的初始化需要传入一个整型参数,此参数标识该信号量可以占用的资源个数。例如我们有两个信号量A,B。A信号量可以允许两个线程占用,B信号量允许一个线程占用,那么初始化的时候Semaphore A = new Semaphore(2);
public class AlternateExample {
private static Semaphore s1 = new Semaphore(1);
private static Semaphore s2 = new Semaphore(1);
private static Semaphore s3 = new Semaphore(1);
static Semaphore[] signals = {s1, s2, s3};
public static void foo(int name) {
while (true) {
try {
signals[name - 1].acquire();
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程名:" + name);
signals[(name) % 3].release();
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> foo(1));
Thread thread2 = new Thread(() -> foo(2));
Thread thread3 = new Thread(() -> foo(3));
//先占用1和2,此处我们要保证的顺序是3、1、2
s1.acquire();
s2.acquire();
thread1.start();
thread2.start();
thread3.start();
Thread.sleep(300);
}
}
三、总结
本篇我们用两个问题引出了3个并发工具CountDownLatch、CyclicBarrier、Semaphore的实际应用的例子。下一篇我们讲从源码角度详细分析下这三个工具的实现细节。
参考文章
【完整代码】使用Semaphore实现线程的交替执行打印 A1B2C3D4E5_学亮编程手记-CSDN博客
CountDownLatch详解 - 简书 (jianshu.com)
Java中多个线程交替循环执行 - 坐看云起时_雨宣 - 博客园 (cnblogs.com)
JAVA Semaphore详解 - 简单爱_wxg - 博客园 (cnblogs.com)
Java并发编程(二)如何保证线程同时/交替执行的更多相关文章
- java并发编程(四) 线程池 & 任务执行、终止源码分析
参考文档 线程池任务执行全过程:https://blog.csdn.net/wojiaolinaaa/article/details/51345789 线程池中断:https://www.cnblog ...
- Java并发编程二三事
Java并发编程二三事 转自我的Github 近日重新翻了一下<Java Concurrency in Practice>故以此文记之. 我觉得Java的并发可以从下面三个点去理解: * ...
- Java并发编程:如何创建线程?
Java并发编程:如何创建线程? 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程去执行一个子任务.下面先讲述一下Java中的应用程序和进程相关的概念知识, ...
- 【转】Java并发编程:如何创建线程?
一.Java中关于应用程序和进程相关的概念 在Java中,一个应用程序对应着一个JVM实例(也有地方称为JVM进程),一般来说名字默认是java.exe或者javaw.exe(windows下可以通过 ...
- 2、Java并发编程:如何创建线程
Java并发编程:如何创建线程? 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程去执行一个子任务.下面先讲述一下Java中的应用程序和进程相关的概念知识, ...
- Java并发编程 | 从进程、线程到并发问题实例解决
计划写几篇文章讲述下Java并发编程,帮助一些初学者成体系的理解并发编程并实际使用,而不只是碎片化的了解一些Synchronized.ReentrantLock等技术点.在讲述的过程中,也想融入一些相 ...
- Java 并发编程——Executor框架和线程池原理
Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...
- [Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ...
[Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ... 摘要 介绍 Java 并发包里的几个主要 ExecutorService . 正文 ...
- Java 并发编程——Executor框架和线程池原理
Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...
- 原创】Java并发编程系列2:线程概念与基础操作
[原创]Java并发编程系列2:线程概念与基础操作 伟大的理想只有经过忘我的斗争和牺牲才能胜利实现. 本篇为[Dali王的技术博客]Java并发编程系列第二篇,讲讲有关线程的那些事儿.主要内容是如下这 ...
随机推荐
- Reverse 高校网络信息安全运维挑战赛
Reverse 高校网络信息安全运维挑战赛 1 signed int sub_403CC0() 2 { 3 unsigned int v0; // eax 4 int key_lens; // eax ...
- 【MCU】移植AT32库&FreeRTOS教程
目录 前言 1. 移植AT库 1.1 移植内核相关文件 1.2 移植芯片型号相关文件 1.3 移植芯片外设驱动库 1.4 移植配置文件及中断回调函数文件 2. 移植FreeRTOS源码 2.1 获取 ...
- day03---Vue(04)
一.组件 组件(Component)是 Vue.js 最强大的功能之一. 组件可以扩展 HTML 元素,封装可重用的代码. 组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界 ...
- Android学习中出现的问题
•问题1:多行文字如何实现跑马灯效果? 博客链接:Androidd Studio 之多行文字跑马灯特效 解决状态:已解决 •问题2:cause: unable to find valid certif ...
- CodeForces CF862E题解
\(Part\ 1:\) 我们发现每次修改动的是\(a\)串,所以对于这个答案的公式,\(b_{i+j}\)的部分是可以求出来的.所以我们可以把公式改成如下所示: \(f(j)=|\sum_{i=1} ...
- ionic3 清除navpush的堆栈 (android真机返回键bug)
清除navpush的堆栈可以使用 方法一:ionic3文档 this.navCtrl.popToRoot(); //直接返回根组件 方法二: push 后再删除之前页面即可. this.navCtrl ...
- ELK7.11.2版本安装部署及ElastAlert告警相关配置
文档开篇,我还是要说一遍,虽然我在文档内容中也会说好多遍,但是希望大家不要嫌我墨迹: 请多看官方文档,请多看命令行报错信息,请多看日志信息,很多时候它们比百度.比必应.比谷歌有用: 请不要嫌麻烦,打开 ...
- Windows10家庭版安装docker
在公司,一直使用mac系统,在mac上安装使用docker还是比较方便的,可本人心血来朝,家里是win10 home版,就想在windows上刷一刷. 好了,废话不多说,直接上干货. 为了不误导广大爱 ...
- 1-Hyperledger Fabric概念详解
目录 一.Hyperledger Fabric概述 二.基本术语 1.共享账本ledger 2.通道Channel 3.组织Org 4.智能合约Chaincode 5.背书Endorse 6.各种节点 ...
- SpringBoot项目打包部署
部署方式 SpringBoot项目可以通过jar包或者war包部署在服务器上,因为jar包更适合前后端分离的项目,所以这里我们使用jar包的方式. 添加maven支持 <!-- 这个插件,可以将 ...