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. Python基础—编码(Day2)

    一.字符编码 1.ASCII码:包含英文.数字.特殊字符,8位=1字节byte =1个字符,如: 0010 1010 ASCII码表里的字符总共有256个,前128个为常用的字符如运算符,后128个称 ...

  2. app内嵌H5网页(webviewJavaScriptBridge)

    摘要:使用的插件为webviewJavaScriptBridge,app端需要引入一下这个包,html页面只需一段JS代码 与IOS交互 <! 申明交互(此处代码固定) > functio ...

  3. 学习java知道这五个网站就够了

    "这个国家的每个人都应该学习编程计算机,因为它教你如何思考." 当乔布斯几年前这么说时,他再次被证明是一个真正的有远见的人. 好吧,这很难反驳!如今,编程比以往任何时候都更加蓬勃发 ...

  4. Go内存逃逸分析

    Go的内存逃逸及逃逸分析 Go的内存逃逸 分析内存逃逸之前要搞清楚一件事 我们编写的程序中的函数和局部变量是存放在栈上的(补充一点堆上存储的数据的指针 是存放在栈上的 因为指针的大小是可以提前预知的 ...

  5. 【C# .Net GC】手动监视和控制对象的生命周期(GCHandle)

    这个话题还未做详细研究,暂时用不到,只是粗略看了一下. 使用System.Runtime.InteropServices.GCHandle类来手动控制对象的生命周期 (个人感觉这里可能有一些问题... ...

  6. 【C#反射】动态创建类型实例

    转载自:https://www.cnblogs.com/dytes/archive/2012/06/29/2569488.html .NET中除了构造函数外,还有多种方式可以创建类型的实例.下面总结了 ...

  7. C# init用法

    init是什么意思? init就 modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit) 类型的缩写 modreq ...

  8. Oracle账户被锁:the account is locked

    转至:https://blog.csdn.net/weixin_37615080/article/details/80400239?utm_medium=distribute.pc_relevant_ ...

  9. Linux系统最重要的工具——Shell学习笔记

    一.为什么学习Shell脚本语言 1.Shell脚本语言是实现Linux/UNIX系统管理及自动化运维必备的重要工具,Linux/UNIX系统底层及 基础应用软件的核心大都涉及Shell脚本的内容. ...

  10. Ghost:凛冬散尽,长夜终明

    你须领先于一切离别,仿佛它们全在你身后 像刚刚逝去的冬天 因为许多冬天中有一个无尽的冬天 使你过冬之心终究捱[ái]过                                          ...