Java线程通信

博客 分享
0 218
张三
张三 2022-03-27 19:57:15
悬赏:0 积分 收藏

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             @Override13             public void run() {14                 doSomething("a");15             }16         });17         Thread b = new Thread(new Runnable() {18             @Override19             public void run() {20                 doSomething("b");21             }22         });23         a.start();24         b.start();25     }26 27     /**28      * 依次打印 1, 2, 3 三个数字29      *30      * @param threadName31      */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执行完毕后才轮到b10      */11     private static void situationOne() {12         Thread a = new Thread(new Runnable() {13             @Override14             public void run() {15                 doSomething("a");16             }17         });18         Thread b = new Thread(new Runnable() {19             @Override20             public void run() {21                 try {22                     System.out.println("线程 b 正在通过thread.join()等待线程 a 执行完毕后再润");23                     // thread.join() 在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行,即必须a执行完毕后才轮到b24                     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 threadName39      */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             @Override12             public void run() {13                 // 同步锁 lock14                 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             @Override30             public void run() {31                 // 同步锁 lock32                 synchronized (lock) {33                     // a 获得锁后执行34                     doSomething("b", 1);35                     doSomething("b", 2);36                     doSomething("b", 3);37                     // 调用 lock.notify() 方法,唤醒正在 wait 的线程 a38                     lock.notify();39                 }40             }41         });42         a.start();43         b.start();44     }45 46     /**47      * 打印48      *49      * @param threadName50      * @param num51      */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、D15         CountDownLatch latch = new CountDownLatch(3);16         new Thread(new Runnable() {17             @Override18             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 C32         for (char threadName = 'A'; threadName <= 'C'; threadName++) {33             String name = String.valueOf(threadName);34             new Thread(new Runnable() {35                 @Override36                 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。

 

 

 

 

 

 

螣蛇乘雾

终为土灰

 

 

 

 

 

 

posted @ 2022-03-27 19:45 涛姐涛哥 阅读(6) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员