线程之间的通信

Posted Chinda

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程之间的通信相关的知识,希望对你有一定的参考价值。

线程通信概念:线程是操作系统中独立的个体 , 但这些个体如果不经过特殊处理就不能成为一个整体 , 线程间的通信就成为整体的必用方法之一。当线程存在通信指挥 , 系统间的交互性会更强大 , 在提高 CPU 利用率的同时还会使开发人员对线程任务在处理的过程中进行有效的把控与监督。

使用 wait / notify 方法实现线程间的通信。 (注意 : 这两个方法都是 Object 类的方法 , 换句话说 java 的所有的对象都提供了这两个方法)。

  • wait 和 notify 必须配合 synchronized 关键字使用。
  • wait 方法释放锁 , notify 方法不释放锁。
 1 package com.itdoc.multi.sync008;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * @BLOG http://www.cnblogs.com/goodcheap
 8  * @DESCRIBE 模拟两线程通信
 9  * @AUTHOR WángChéngDá
10  * @DATE 2017-03-24 13:14
11  */
12 public class ListAdd1 {
13 
14     private List list = new ArrayList();
15 
16     public void add() {
17         list.add("-----");
18     }
19     public int size() {
20         return list.size();
21     }
22 
23     public static void main(String[] args) {
24         final ListAdd1 list1 = new ListAdd1();
25         new Thread(() -> {
26             System.out.println(Thread.currentThread().getName() + "已执行...");
27             try {
28                 for (int i = 0; i < 10; i++) {
29                     list1.add();
30                     System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
31                     Thread.sleep(400);
32                 }
33                 System.out.println(Thread.currentThread().getName() + "已结束...");
34             } catch (InterruptedException e) {
35                 e.printStackTrace();
36             }
37         }, "T1").start();
38 
39         new Thread(() -> {
40             System.out.println(Thread.currentThread().getName() + "已执行...");
41             while (true) {
42                 if (list1.size() == 5) {
43                     System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + " list size = 5 线程停止..");
44                     break;
45                 }
46             }
47             System.out.println(Thread.currentThread().getName() + "已结束...");
48         }, "T2").start();
49     }
50 }

 

控制台输出:

T1已执行...
当前线程:T1添加了一个元素..
T2已执行...
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
T1已结束...

线程 T2 进入死循环。原因:因为 list 不是多线程可见 , 所以 if 判断不成立 , 所以进入死循环。(CPU:4 代 I3 和 3 代 I5 以上处理器 , 不会出现死循环)。在 while 语句中添加 print 语句也不会出现死循环。

 

模拟两线程通信

 1 package com.itdoc.multi.sync008;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * @BLOG http://www.cnblogs.com/goodcheap
 8  * @DESCRIBE 模拟两线程通信
 9  * @AUTHOR WángChéngDá
10  * @DATE 2017-03-24 13:14
11  */
12 public class ListAdd1 {
13 
14     private volatile List list = new ArrayList();
15 
16     public void add() {
17         list.add("-----");
18     }
19     public int size() {
20         return list.size();
21     }
22 
23     public static void main(String[] args) {
24         final ListAdd1 list1 = new ListAdd1();
25         new Thread(() -> {
26             System.out.println(Thread.currentThread().getName() + "已执行...");
27             try {
28                 for (int i = 0; i < 10; i++) {
29                     list1.add();
30                     System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
31                     Thread.sleep(400);
32                 }
33                 System.out.println(Thread.currentThread().getName() + "已结束...");
34             } catch (InterruptedException e) {
35                 e.printStackTrace();
36             }
37         }, "T1").start();
38 
39         new Thread(() -> {
40             System.out.println(Thread.currentThread().getName() + "已执行...");
41             while (true) {
42                 if (list1.size() == 5) {
43                     System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + " list size = 5 线程停止..");
44                     break;
45                 }
46             }
47             System.out.println(Thread.currentThread().getName() + "已结束...");
48         }, "T2").start();
49     }
50 }

 

控制台输出:

T1已执行...
当前线程:T1添加了一个元素..
T2已执行...
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程收到通知:T2 list size = 5 线程停止..
T2已结束...
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
T1已结束...

虽然实现了线程通信 , 但是线程 T2 是处于轮询状态 , 浪费资源。

 

wait / notify 实现线程通信

 1 package com.itdoc.multi.sync008;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * @BLOG http://www.cnblogs.com/goodcheap
 8  * @DESCRIBE wait / notify 实现线程通信
 9  * @AUTHOR WángChéngDá
10  * @DATE 2017-03-24 14:35
11  */
12 public class ListAdd2 {
13 
14     private List list = new ArrayList<>();
15 
16     public void add() {
17         list.add("----------");
18     }
19 
20     public int size() {
21         return list.size();
22     }
23 
24 
25     public static void main(String[] args) {
26         final ListAdd2 list2 = new ListAdd2();
27 
28         final Object lock = new Object();
29 
30         /**
31          * 线程 T2 必须要比线程 T1 先执行, 若线程 T1 先执行,
32          * 获取到对象锁, 当线程 T1 释放对象锁的时候, size 永远不能等于 5。
33          */
34         new Thread(() -> {
35             System.out.println(Thread.currentThread().getName() + "已执行...");
36             try {
37                 synchronized (lock) {
38                     if (list2.size() != 5) {
39                         lock.wait();
40                     }
41                     System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
42                 }
43             } catch (InterruptedException e) {
44                 e.printStackTrace();
45             }
46             System.out.println(Thread.currentThread().getName() + "已结束...");
47         }, "T2").start();
48 
49         new Thread(() -> {
50             System.out.println(Thread.currentThread().getName() + "已执行...");
51             try {
52                 synchronized (lock) {
53                     for (int i = 0; i < 10; i++) {
54                         list2.add();
55                         System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
56                         Thread.sleep(500);
57                         if (list2.size() == 5) {
58                             System.out.println("已经发出通知..");
59                             lock.notify();
60                         }
61                     }
62                 }
63             } catch (InterruptedException e) {
64                 e.printStackTrace();
65             }
66             System.out.println(Thread.currentThread().getName() + "已结束...");
67         }, "T1").start();
68     }
69 }

线程 T2 必须要比线程 T1 先执行, 若线程 T1 先执行,获取到对象锁, 当线程 T1 释放对象锁的时候, size 永远不能等于 5。经测试:假如将 list 用 volatile 修饰, 线程 T2 放在线程 T1 前面开启 , T2先启动的概率大一些。

 

控制台输出:

T2已执行...
T1已执行...
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
已经发出通知..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
T1已结束...
当前线程:T2收到通知线程停止..
T2已结束...

只有线程 T1 执行完成之后 , 线程 T2 才收到通知 , 开始执行。

 

CountDownLatch 实现线程间通信

 1 package com.itdoc.multi.sync008;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 import java.util.concurrent.CountDownLatch;
 6 
 7 /**
 8  * @BLOG http://www.cnblogs.com/goodcheap
 9  * @DESCRIBE CountDownLatch 实现线程间通信
10  * @AUTHOR WángChéngDá
11  * @DATE 2017-03-24 15:02
12  */
13 public class ListAdd3 {
14 
15     private List list = new ArrayList<>();
16 
17     public void add() {
18         list.add("-----");
19     }
20     public int size() {
21         return list.size();
22     }
23 
24     public static void main(String[] args) {
25         final ListAdd3 list3 = new ListAdd3();
26 
27         final CountDownLatch countDownLatch = new CountDownLatch(1);//传送一次通知, 唤醒阻塞线程。
28 
29         new Thread(() -> {
30             System.out.println(Thread.currentThread().getName() + "已执行...");
31             try {
32                 for (int i = 0; i < 10; i++) {
33                     list3.add();
34                     System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
35                     Thread.sleep(500);
36                     if (list3.size() == 5) {
37                         System.out.println("已经发出通知..");
38                         countDownLatch.countDown();
39                     }
40                 }
41                 System.out.println(Thread.currentThread().getName() + "已结束...");
42             } catch (InterruptedException e) {
43                 e.printStackTrace();
44             }
45         }, "T1").start();
46 
47         new Thread(() -> {
48             System.out.println(Thread.currentThread().getName() + "已执行...");
49             try {
50                 if (list3.size() != 5) {
51                     countDownLatch.await();
52                 }
53                 System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
54             } catch (InterruptedException e) {
55                 e.printStackTrace();
56             }
57             System.out.println(Thread.currentThread().getName() + "已结束...");
58         }, "T2").start();
59     }
60 }

 

 控制台输出:

T1已执行...
当前线程:T1添加了一个元素..
T2已执行...
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
已经发出通知..
当前线程:T1添加了一个元素..
当前线程:T2收到通知线程停止..
T2已结束...
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
当前线程:T1添加了一个元素..
T1已结束...

以上是关于线程之间的通信的主要内容,如果未能解决你的问题,请参考以下文章

如何在嵌套片段内的两个子片段之间进行通信

片段和服务器之间的通信问题

与另一个片段通信的片段接口

在片段和活动之间进行通信 - 最佳实践

如何使用接口在片段和活动之间进行通信?

是否最好使用 Activity.onAttachFragment 或 Fragment.onAttach 在 Activity 和嵌套片段之间进行通信?