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线程通信的更多相关文章

  1. 第23章 java线程通信——生产者/消费者模型案例

    第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...

  2. Java 线程通信

    线程通信用来保证线程协调运行,一般在做线程同步的时候才需要考虑线程通信的问题. 1.传统的线程通信 通常利用Objeclt类提供的三个方法: wait() 导致当前线程等待,并释放该同步监视器的锁定, ...

  3. java线程通信与协作小结 多线程中篇(十六)

      在锁与监视器中我们对Object中的方法进行了简单介绍 以监视器原理为核心,三个方法:wait,notify.notifyAll,可以完成线程之间的通信 当然,不会像“语言”似的,有多种多样的沟通 ...

  4. Java线程通信-生产者消费者问题

    线程通信示例——生产者消费者问题 这类问题描述了一种情况,假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费.假设仓库中没有产品,则生产者可以将 产品放入仓库,有 ...

  5. Java线程通信——wait() 和 notify()

    Object类中有关线程通信的方法有两个notify方法和三个wait方法,官方解释: void notify() Wakes up a single thread that is waiting o ...

  6. JAVA线程通信之生产者与消费者

    package cn.test.hf.test3; import java.util.concurrent.locks.Condition;import java.util.concurrent.lo ...

  7. Java线程入门第二篇

    Java线程通信方法 0.(why)每个线程都有自己的栈空间,我们要线程之间进行交流,合作共赢. 1.synchronized和volatile关键字 a)  看下面的synchronized关键字 ...

  8. java多线程-线程通信

    线程通信的目标是使线程间能够互相发送信号.另一方面,线程通信使线程能够等待其他线程的信号. 通过共享对象通信 忙等待 wait(),notify()和 notifyAll() 丢失的信号 假唤醒 多线 ...

  9. Java核心知识点学习----使用Condition控制线程通信

    一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-> ...

随机推荐

  1. Solution -「ARC 058C」「AT 1975」Iroha and Haiku

    \(\mathcal{Description}\)   Link.   称一个正整数序列为"俳(pái)句",当且仅当序列中存在连续一段和为 \(x\),紧接着连续一段和为 \(y ...

  2. 第二章 初始MySQL 语法

    1.使用MySQL的优势 运行速度快.MySQL体积小,命令执行的速度快: 使用成本低.MySQL是开源的. 容易使用. 可移植性强.MySQL能够运行于多种系统平台之上,windows,linux, ...

  3. suse 12 部署chrony时间同步服务器

    文章目录 1.ntp和chrony的区别 1.1.关于chrony 1.2.chronyd的优势 2.环境介绍 3.部署chrony 4.配置chrony 4.1.配置文件解析 4.2.查看chron ...

  4. php 利用 fsockopen GET/POST 提交表单及上传文件

    1.GET get.php <?php$host = 'demo.fdipzone.com';$port = 80;$errno = '';$errstr = '';$timeout = 30; ...

  5. MySQL windows下cmd安装操作

    sh1.下载安装包,解压到指定目录  网址:https://dev.mysql.com/downloads/mysql/ 2.添加环境变量 右键点击计算机-属性-高级系统设置-环境变量: 将mysql ...

  6. JVM学习——G1垃圾回收器(学习过程)

    JVM学习--G1垃圾回收器 把这个跨时代的垃圾回收器的笔记独立出来. 新生代:适用复制算法 老年代:适用标记清除.标记整理算法 二娃本来看G1的时候觉得比较枯燥,但是后来总结完之后告诉我说,一定要慢 ...

  7. RocketMQ的invokeSync call timeout异常的解决办法

    缘起 在RocketMQ客户端的DefaultMQPushConsumer的start方法被执行时,时不时会报出invokeSync call timeout异常,如下: Caused by: jav ...

  8. Go Exec 僵尸与孤儿进程

    原文地址:Go Exec 僵尸与孤儿进程 最近,使用 golang 去管理本地应用的生命周期,期间有几个有趣的点,今天就一起看下. 场景一 我们来看看下面两个脚本会产生什么问题: 创建两个 shell ...

  9. 数据分析刚入门?这几个BI软件你一定得知道!

    在当前乃至未来5年的职场中,将取代你的可能不是 AI ,而是比你"更懂"数据分析的同事.毕竟,现在做什么都讲究"用数字说话",很多岗位在招聘JD中均给出了&qu ...

  10. C# 枚举器(enumerator)

    总结: 1.枚举器就像是序列中的"游标"或"书签".可以有多个"书签",移动其中任何一个都可以枚举集合,与其他枚举器互不影响.用来遍历数据结 ...