• 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 }
    View Code

    多次运行发现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 }
    View Code

    无论运行多少次,都是线程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 }
    View Code

    无论运行多少次,都是线程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 }
    View Code

     五、CyclicBarrier 

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

     

     

     

    螣蛇乘雾

    终为土灰

     

     

     

     

    
    
    
  • 相关阅读:
    文件操作
    POJO对象建立规则
    第三章 Java 的基本程序设计结构
    第一章 Java程序设计概述
    Dao层设计
    业务功能迭代开发过程
    mysql 修改语法格式
    自定义 Java 异常 (Exception)
    接口开发注意事项-个人总结
    intelliJ IDEA springMVC 搭建配置
  • 原文地址:https://www.cnblogs.com/taojietaoge/p/15997641.html
Copyright © 2020-2023  润新知