Java线程通信
Java线程通信
螣蛇乘雾,终为土灰。
多个线程协同工作完成某个任务时就会涉及到线程间通信问题。如何使各个线程之间同时执行,顺序执行、交叉执行等。
一、线程同时执行
创建两个线程a和b,两个线程内调用同一个打印 1-3 三个数字的方法。

1 package tjt;
2
3 import java.time.LocalDate;
4
5 public class Test {
6
7 /**
8 * 创建两个线程a和b,两个线程内调用同一个打印 1-3 三个数字的方法。
9 */
10 private static void situationOne() {
11 Thread a = new Thread(new Runnable() {
12 @Override
13 public void run() {
14 doSomething("a");
15 }
16 });
17 Thread b = new Thread(new Runnable() {
18 @Override
19 public void run() {
20 doSomething("b");
21 }
22 });
23 a.start();
24 b.start();
25 }
26
27 /**
28 * 依次打印 1, 2, 3 三个数字
29 *
30 * @param threadName
31 */
32 private static void doSomething(String threadName) {
33 int i = 0;
34 while (i++ < 3) {
35 try {
36 Thread.sleep(200);
37 } catch (InterruptedException e) {
38 e.printStackTrace();
39 }
40 System.out.println(LocalDate.now() + " Thread " + threadName + " is doing, printing: " + i);
41 }
42 }
43
44 public static void main(String[] args) {
45 situationOne();
46 }
47 }
多次运行发现a和b是同时打印的,无执行顺序可言。

二、线程顺序执行
创建两个线程a和b,要求b 在 a 全部打印完后再开始打印。使用 thread.join() 方法,在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行,即必须a执行完毕后才轮到b。

1 package tjt;
2
3 import java.time.LocalDate;
4
5 public class Test {
6
7 /**
8 * 创建两个线程a和b,要求b 在 a 全部打印完后再开始打印,使用 thread.join() 方法。
9 * 保证线程a执行完毕后才轮到b
10 */
11 private static void situationOne() {
12 Thread a = new Thread(new Runnable() {
13 @Override
14 public void run() {
15 doSomething("a");
16 }
17 });
18 Thread b = new Thread(new Runnable() {
19 @Override
20 public void run() {
21 try {
22 System.out.println("线程 b 正在通过thread.join()等待线程 a 执行完毕后再润");
23 // thread.join() 在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行,即必须a执行完毕后才轮到b
24 a.join();
25 } catch (InterruptedException e) {
26 e.printStackTrace();
27 }
28 doSomething("b");
29 }
30 });
31 a.start();
32 b.start();
33 }
34
35 /**
36 * 依次打印 1, 2, 3 三个数字
37 *
38 * @param threadName
39 */
40 private static void doSomething(String threadName) {
41 int i = 0;
42 while (i++ < 3) {
43 try {
44 Thread.sleep(200);
45 } catch (InterruptedException e) {
46 e.printStackTrace();
47 }
48 System.out.println(LocalDate.now() + " Thread " + threadName + " is doing, printing: " + i);
49 }
50 }
51
52 public static void main(String[] args) {
53 situationOne();
54 }
55 }
无论运行多少次,都是线程a先执行完毕再到线程b。

三、线程顺序交叉执行
创建两个线程a和b,要求 a 在打印完 1 后,再让 b 打印 1、2、 3,接着再回到 a 继续打印 2、3。如此顺序交叉执行仅靠 Thread.join() 是无法满足需求的,需要更细粒度的锁来控制执行顺序,以及object.wait() 和 object.notify() 两个方法来实现。

1 package tjt;
2
3 import java.time.LocalDate;
4
5 public class TestAgain {
6
7 private static void situationTwo() {
8 // a 和 b 的共享对象锁 lock
9 Object lock = new Object();
10 Thread a = new Thread(new Runnable() {
11 @Override
12 public void run() {
13 // 同步锁 lock
14 synchronized (lock) {
15 // a 获得锁后执行
16 doSomething("a", 1);
17 try {
18 // 调用 lock.wait() 方法,交出锁的控制权,进入 wait 状态,等待notify唤醒
19 lock.wait();
20 } catch (InterruptedException e) {
21 e.printStackTrace();
22 }
23 doSomething("a", 2);
24 doSomething("a", 3);
25 }
26 }
27 });
28 Thread b = new Thread(new Runnable() {
29 @Override
30 public void run() {
31 // 同步锁 lock
32 synchronized (lock) {
33 // a 获得锁后执行
34 doSomething("b", 1);
35 doSomething("b", 2);
36 doSomething("b", 3);
37 // 调用 lock.notify() 方法,唤醒正在 wait 的线程 a
38 lock.notify();
39 }
40 }
41 });
42 a.start();
43 b.start();
44 }
45
46 /**
47 * 打印
48 *
49 * @param threadName
50 * @param num
51 */
52 private static void doSomething(String threadName, int num) {
53 System.out.println(LocalDate.now() + " Thread " + threadName + " is doing, printing: " + num);
54 }
55
56 public static void main(String[] args) {
57 situationTwo();
58 }
59 }
无论运行多少次,都是线程a先执行打印1,然后线程b执行打印1、2、3,最后线程a执行打印2、3。

四、CountDownLatch
CountDownLatch 计数器适用于一个线程去等待多个线程的情况。例如A B C 三个线程同时运行,各自独立运行完后通知线程 D 执行,就可以利用 CountdownLatch 来实现这类通信方式。
对比之前的join方法,thread.join()可以让一个线程等另一个线程运行完毕后再继续执行,其可以在 D 线程里依次 join A B C,但这样 A B C 必须依次执行,无法实现ABC三者能同步运行。

1 package tjt;
2
3 import java.time.LocalDate;
4 import java.util.concurrent.CountDownLatch;
5
6 public class TestCountDownLatch {
7
8 /**
9 * countDownLatch 适用于一个线程去等待多个线程的情况
10 * 四个线程A、B、C、D,
11 * 其中 D 要等到 A B C 全执行完毕后才执行,且 A B C 是同步运行的,即ABC无顺序执行
12 */
13 private static void situationThree() {
14 // 初始计数值设置为3,即总共四个线程A、B、C、D
15 CountDownLatch latch = new CountDownLatch(3);
16 new Thread(new Runnable() {
17 @Override
18 public void run() {
19 System.out.println(LocalDate.now() + "线程 D 等待线程A B C 执行完毕后才可执行");
20 try {
21 // await() 检查计数器值是否为 0,若不为 0 则保持等待状态
22 latch.await();
23 // 其他线程 的 countDown() 方法把计数值变成 0 时,等待线程里的 countDownLatch.await() 立即退出,继续执行下面的代码
24 System.out.println(LocalDate.now() + "线程A B C 执行完毕,轮到线程D 执行了,当前latch:" + latch.getCount());
25 } catch (InterruptedException e) {
26 e.printStackTrace();
27 }
28 }
29 }).start();
30
31 // 循环执行线程 A B C
32 for (char threadName = 'A'; threadName <= 'C'; threadName++) {
33 String name = String.valueOf(threadName);
34 new Thread(new Runnable() {
35 @Override
36 public void run() {
37 System.out.println("线程 " + name + " is running");
38 // countDown(),将倒计数器减 1, 计数器被减至 0 时立即触发D 的 await()
39 try {
40 Thread.sleep(200);
41 } catch (InterruptedException e) {
42 e.printStackTrace();
43 }
44 System.out.println("线程 " + name + " 执行完毕计数器");
45 latch.countDown();
46 }
47 }).start();
48 }
49 }
50
51 public static void main(String[] args) {
52 situationThree();
53 }
54
55 }

五、CyclicBarrier
实现线程间互相等待,可以利用 CyclicBarrier 栅栏。CountDownLatch 可以用来倒计数,但当计数完毕,只有一个线程的 await() 会得到响应,无法让多个线程同时触发。如要求线程 A B C 各自开始准备,直到三者都准备完毕再同时运行其就无法满足需求,而用CyclicBarrier则完全OK。

螣蛇乘雾
终为土灰
Java线程通信的更多相关文章
- 第23章 java线程通信——生产者/消费者模型案例
第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...
- Java 线程通信
线程通信用来保证线程协调运行,一般在做线程同步的时候才需要考虑线程通信的问题. 1.传统的线程通信 通常利用Objeclt类提供的三个方法: wait() 导致当前线程等待,并释放该同步监视器的锁定, ...
- java线程通信与协作小结 多线程中篇(十六)
在锁与监视器中我们对Object中的方法进行了简单介绍 以监视器原理为核心,三个方法:wait,notify.notifyAll,可以完成线程之间的通信 当然,不会像“语言”似的,有多种多样的沟通 ...
- Java线程通信-生产者消费者问题
线程通信示例——生产者消费者问题 这类问题描述了一种情况,假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费.假设仓库中没有产品,则生产者可以将 产品放入仓库,有 ...
- Java线程通信——wait() 和 notify()
Object类中有关线程通信的方法有两个notify方法和三个wait方法,官方解释: void notify() Wakes up a single thread that is waiting o ...
- JAVA线程通信之生产者与消费者
package cn.test.hf.test3; import java.util.concurrent.locks.Condition;import java.util.concurrent.lo ...
- Java线程入门第二篇
Java线程通信方法 0.(why)每个线程都有自己的栈空间,我们要线程之间进行交流,合作共赢. 1.synchronized和volatile关键字 a) 看下面的synchronized关键字 ...
- java多线程-线程通信
线程通信的目标是使线程间能够互相发送信号.另一方面,线程通信使线程能够等待其他线程的信号. 通过共享对象通信 忙等待 wait(),notify()和 notifyAll() 丢失的信号 假唤醒 多线 ...
- Java核心知识点学习----使用Condition控制线程通信
一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-> ...
随机推荐
- Solution -「AGC 002F」「AT 2000」Leftmost Ball
\(\mathcal{Description}\) Link. 给你 \(n\) 种颜色的球,每个球有 \(k\) 个,把这 \(n\times k\) 个球排成一排,把每一种颜色的最左边出现 ...
- Linux上大文件切割以及批量并发处理
一.环境说明 某次项目需求中,在Linux上有批文本文件,文件文件都有几个G大,几千万行的数据.无论在Linux和Windows打开这么大的文件,基本上打开要卡半天,更别说编辑. 因此想到使用spli ...
- 依赖于angular的table组件
组件实现了以下功能 1. 列宽可动态拖动 2. 列数据排序 3. 列过滤 4. 列位置自由调整 除了需要引入angular.js(我用的是1.4.6版本),还需要引用一个angular衍生出来的插件n ...
- DDD-领域驱动设计简谈
看到网上讨论 DDD 的文章越来越多,咱也不能甘于人后啊,以下是我对 DDD 的个人理解,短小精悍,不喜忽喷. 也谈DDD(领域驱动设计) 解决什么问题 传统模式,产品评审结束,开发人员就凭经验拆分模 ...
- k8s-ingress增加跨域问题
第一种: kubectl get ingresses. -n rrzhibo-admin test-rrzb-apiadmin-gateway-http-ingress -o yaml apiVers ...
- PostgreSQL VACUUM 之深入浅出 (二)
AUTOVACUUM AUTOVACUUM 简介 PostgreSQL 提供了 AUTOVACUUM 的机制. autovacuum 不仅会自动进行 VACUUM,也会自动进行 ANALYZE,以分析 ...
- HttpClient的使用(get、post请求)
添加pom依赖 <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> <d ...
- Java并发杂谈(一):volatile的底层原理,从字节码到CPU
volatile的特性 volatile是Java中用于修饰变量的关键字,其主要是保证了该变量的可见性以及顺序性,但是没有保证原子性:其是Java中最为轻量级的同步关键字: 接下来我将会一步步来分析v ...
- vue2项目,踩坑Jest单元测试
目前的项目已经维护了挺久,由于客户要求,我们要为项目加上单元测试,挑选一番后选择了Jest(配置简便,开箱即用),下面记录了此次为项目添加Jest作为单元测试的经历. 安装Jest 1. 在项目目录下 ...
- 『无为则无心』Python日志 — 65、日志模块logging的使用
目录 1.logger类用法 2.handler类用法 3.formatter类用法 4.filter类用法 1.logger类用法 logger类:logger用于提供日志接口,常用于配置和发送日志 ...