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并发编程系列第二篇,讲讲有关线程的那些事儿.主要内容是如下这 ...
随机推荐
- 利用flex解决input定位的问题
用简单的布局搞定input框使用fixed下输入的问题 最近在做移动端H5聊天应用发现,当input框在最底部并且使用 position:fixed 属性的时候在苹果手机中会出现不兼容的情况
- Typora的MarkDown语法快捷键
Typora的MarkDown语法快捷键 1.标题 项目 快捷键一 快捷键二 一级标题 #+空格+文本+回车 Ctrl+1 二级标题 ##+空格+文本+回车 ctrl+2 三级-- ###-- ctr ...
- PAT (Basic Level) Practice (中文)1065 单身狗 (25 分) 凌宸1642
PAT (Basic Level) Practice (中文)1065 单身狗 (25 分) 凌宸1642 题目描述: "单身狗"是中文对于单身人士的一种爱称.本题请你从上万人的大 ...
- Python异步asyncio快速实践模版
只是参考快速跑起来模版,细节或者封装流畅使用需要详细阅读aiohttp文档 1 import asyncio 2 3 async def foo(): 4 await print('bar') 5 6 ...
- Elasticsearch 主节点和暖热节点解析
Elasticsearch 主节点和暖热节点解析 主节点 控制整个集群,进行一些轻量级操作,列如:跟踪哪些节点是集群中的一部分,决定节点分片分配,负责集群健康, 不包含数据,也不参与搜索和索引操作,对 ...
- [源码解析] 并行分布式任务队列 Celery 之 多进程模型
[源码解析] 并行分布式任务队列 Celery 之 多进程模型 目录 [源码解析] 并行分布式任务队列 Celery 之 多进程模型 0x00 摘要 0x01 Consumer 组件 Pool boo ...
- 痞子衡嵌入式:在i.MXRT启动头FDCB里调整Flash工作频率也需同步设Dummy Cycle
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是Flash工作频率与Dummy Cycle的联系. 上一篇文章 <从头开始认识i.MXRT启动头FDCB里的lookupTable ...
- Linux下屏幕亮度的调节
1 概述 Linux下的屏幕调节可以通过图形界面或者命令行进行调节,图形界面的话主要就是在设置中进行调节,如果设置中没有相应的选项可以进行手动调节. 2 手动调节 2.1 手动屏幕调节 亮度由ACPI ...
- Mysql 添加 create_time, update_time 创建时间 更新时间 自动更新
# 添加 创建 更新 时间字段 ALTER TABLE `表名` ADD COLUMN `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTA ...
- Go-27-import导包
import 导入包 包的搜索顺序: import ( "fmt" "net/http" "mypkg" ) 编译器会根据上面指定的相对路径 ...