Java多线程详解(五)------多线程间的通信

1、常规版的生产者与消费者(synchronized,wait,notify)

  1 package com.study.thread;
  2 
  3 public class TestThread919 {
  4     public static void main(String[] args) {
  5         AirConditioner ac = new AirConditioner();
  6         //A,B线程增加空调温度
  7         new Thread(() -> {
  8             for (int i = 0; i < 10; i++) {
  9                 try {
 10                     ac.increament();
 11                 } catch (Exception e) {
 12                     e.printStackTrace();
 13                 }
 14             }
 15         }, "A").start();
 16         new Thread(() -> {
 17             for (int i = 0; i < 10; i++) {
 18                 try {
 19                     ac.increament();
 20                 } catch (Exception e) {
 21                     e.printStackTrace();
 22                 }
 23             }
 24         }, "B").start();
 25 
 26         //C,D线程减小温度
 27         new Thread(() -> {
 28             for (int i = 0; i < 10; i++) {
 29                 try {
 30                     ac.decreament();
 31                 } catch (Exception e) {
 32                     e.printStackTrace();
 33                 }
 34             }
 35         }, "C").start();
 36         new Thread(() -> {
 37             for (int i = 0; i < 10; i++) {
 38                 try {
 39                     ac.decreament();
 40                 } catch (Exception e) {
 41                     e.printStackTrace();
 42                 }
 43             }
 44         }, "D").start();
 45 
 46 
 47     }
 48 }
 49 
 50 class AirConditioner {//资源类
 51     private int num = 0;//num表示温度大小
 52 
 53     //多线程之间的相互通知,口诀:判断-》干活-》通知
 54     public synchronized void increament() throws Exception {//增加一度
 55         while (num != 0) {//这里判断不用if,用while,while可以防止虚假唤醒
 56             this.wait();
 57         }
 58         num++;
 59         System.out.println(Thread.currentThread().getName() + "	" + num);
 60         this.notifyAll();
 61     }
 62 
 63     public synchronized void decreament() throws Exception {//减小一度
 64         while (num == 0) {
 65             this.wait();
 66         }
 67         num--;
 68         System.out.println(Thread.currentThread().getName() + "	" + num);
 69         this.notifyAll();
 70     }
 71 }
 72 
 73 输出打印:
 74 "C:Program FilesJavajdk1.8.0_131injava.exe" "-javaagent:C:Program FilesJetBrainsIntelliJ IDEA 2019.3.4libidea_rt.jar=56512:C:Program FilesJetBrainsIntelliJ IDEA 2019.3.4in" -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.8.0_131jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_131jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_131jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_131jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_131jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_131jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_131jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_131jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_131jrelibext
ashorn.jar;C:Program FilesJavajdk1.8.0_131jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_131jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_131jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_131jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_131jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_131jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_131jrelibjce.jar;C:Program FilesJavajdk1.8.0_131jrelibjfr.jar;C:Program FilesJavajdk1.8.0_131jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_131jrelibjsse.jar;C:Program FilesJavajdk1.8.0_131jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_131jrelibplugin.jar;C:Program FilesJavajdk1.8.0_131jrelib
esources.jar;C:Program FilesJavajdk1.8.0_131jrelib
t.jar;D:java-projectsuanfaoutproductionjuc" com.study.thread.TestThread919
 75 A    1
 76 C    0
 77 B    1
 78 C    0
 79 A    1
 80 C    0
 81 B    1
 82 C    0
 83 A    1
 84 C    0
 85 B    1
 86 C    0
 87 A    1
 88 C    0
 89 B    1
 90 D    0
 91 A    1
 92 C    0
 93 B    1
 94 D    0
 95 A    1
 96 C    0
 97 B    1
 98 D    0
 99 A    1
100 C    0
101 B    1
102 D    0
103 A    1
104 D    0
105 B    1
106 D    0
107 A    1
108 D    0
109 B    1
110 D    0
111 A    1
112 D    0
113 B    1
114 D    0

2、新版的生产者与消费者(Lock,Condition,await,signal) 

  1 package com.study.thread;
  2 
  3 import java.util.concurrent.locks.Condition;
  4 import java.util.concurrent.locks.Lock;
  5 import java.util.concurrent.locks.ReentrantLock;
  6 
  7 public class TestThread919 {
  8     public static void main(String[] args) {
  9         AirConditioner ac = new AirConditioner();
 10         //A,B线程增加空调温度
 11         new Thread(() -> {
 12             for (int i = 0; i < 10; i++) {
 13                 try {
 14                     ac.increament();
 15                 } catch (Exception e) {
 16                     e.printStackTrace();
 17                 }
 18             }
 19         }, "A").start();
 20         new Thread(() -> {
 21             for (int i = 0; i < 10; i++) {
 22                 try {
 23                     ac.increament();
 24                 } catch (Exception e) {
 25                     e.printStackTrace();
 26                 }
 27             }
 28         }, "B").start();
 29 
 30         //C,D线程减小温度
 31         new Thread(() -> {
 32             for (int i = 0; i < 5; i++) {
 33                 try {
 34                     ac.decreament();
 35                 } catch (Exception e) {
 36                     e.printStackTrace();
 37                 }
 38             }
 39         }, "C").start();
 40         new Thread(() -> {
 41             for (int i = 0; i < 5; i++) {
 42                 try {
 43                     ac.decreament();
 44                 } catch (Exception e) {
 45                     e.printStackTrace();
 46                 }
 47             }
 48         }, "D").start();
 49 
 50 
 51     }
 52 }
 53 
 54 class AirConditioner {//资源类
 55     private int num = 0;//num表示温度大小
 56     private Lock l = new ReentrantLock();
 57     private Condition condition = l.newCondition();
 58 
 59     //多线程之间的相互通知,口诀:判断-》干活-》通知
 60 //    public synchronized void increament() throws Exception {//增加一度
 61 //        while (num != 0) {//这里判断不用if,用while,while可以防止虚假唤醒
 62 //            this.wait();
 63 //        }
 64 //        num++;
 65 //        System.out.println(Thread.currentThread().getName() + "	" + num);
 66 //        this.notifyAll();
 67 //    }
 68 //
 69 //    public synchronized void decreament() throws Exception {//减小一度
 70 //        while (num == 0) {
 71 //            this.wait();
 72 //        }
 73 //        num--;
 74 //        System.out.println(Thread.currentThread().getName() + "	" + num);
 75 //        this.notifyAll();
 76 //    }
 77     public void increament() throws Exception {//增加一度
 78         l.lock();
 79         try {
 80             while (num != 0) {//这里判断不用if,用while,while可以防止虚假唤醒
 81                 condition.await();
 82             }
 83             num++;
 84             System.out.println(Thread.currentThread().getName() + "	" + num);
 85             condition.signalAll();
 86         } finally {
 87             l.unlock();
 88         }
 89     }
 90 
 91     public void decreament() throws Exception {//增加一度
 92         l.lock();
 93         try {
 94             while (num == 0) {//这里判断不用if,用while,while可以防止虚假唤醒
 95                 condition.await();
 96             }
 97             num--;
 98             System.out.println(Thread.currentThread().getName() + "	" + num);
 99             condition.signalAll();
100         } finally {
101             l.unlock();
102         }
103     }
104 }
105 
106 打印输出:
107 "C:Program FilesJavajdk1.8.0_131injava.exe" "-javaagent:C:Program FilesJetBrainsIntelliJ IDEA 2019.3.4libidea_rt.jar=57081:C:Program FilesJetBrainsIntelliJ IDEA 2019.3.4in" -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.8.0_131jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_131jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_131jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_131jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_131jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_131jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_131jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_131jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_131jrelibext
ashorn.jar;C:Program FilesJavajdk1.8.0_131jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_131jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_131jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_131jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_131jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_131jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_131jrelibjce.jar;C:Program FilesJavajdk1.8.0_131jrelibjfr.jar;C:Program FilesJavajdk1.8.0_131jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_131jrelibjsse.jar;C:Program FilesJavajdk1.8.0_131jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_131jrelibplugin.jar;C:Program FilesJavajdk1.8.0_131jrelib
esources.jar;C:Program FilesJavajdk1.8.0_131jrelib
t.jar;D:java-projectsuanfaoutproductionjuc" com.study.thread.TestThread919
108 A    1
109 C    0
110 A    1
111 C    0
112 A    1
113 C    0
114 A    1
115 C    0
116 A    1
117 D    0
118 A    1
119 D    0
120 A    1
121 D    0
122 A    1
123 D    0
124 A    1
125 D    0
126 A    1
127 C    0
128 B    1

3、新版的生产者与消费者(Lock,Condition,await,signal) 的精准通知(较普通版的优势)

  例子:有A,B,C三个线程,要求A打印5次,B打印10次,C打印15次,循环10次

  1 package com.study.thread;
  2 
  3 import java.util.concurrent.locks.Condition;
  4 import java.util.concurrent.locks.Lock;
  5 import java.util.concurrent.locks.ReentrantLock;
  6 
  7 public class ThreadOfCondition {
  8     public static void main(String[] args) {
  9         PrintSourse printSourse = new PrintSourse();
 10         new Thread(() -> {
 11             for (int i = 0; i < 5; i++) {
 12                 printSourse.print5();
 13             }
 14         }, "A").start();
 15         new Thread(() -> {
 16             for (int i = 0; i < 5; i++) {
 17                 printSourse.print10();
 18             }
 19         }, "B").start();
 20         new Thread(() -> {
 21             for (int i = 0; i < 5; i++) {
 22                 printSourse.print15();
 23             }
 24         }, "C").start();
 25     }
 26 }
 27 
 28 class PrintSourse {
 29     //condition实现精准通知线程,
 30 
 31     private int num = 1;//标志位,为不同的线程设置不同的标志位,唤醒通知的时候用
 32     private Lock lock = new ReentrantLock();
 33     private Condition condition1 = lock.newCondition();
 34     private Condition condition2 = lock.newCondition();
 35     private Condition condition3 = lock.newCondition();
 36 
 37     //创建3个方法,分别打印5次,10,15次
 38     public void print5() {
 39         lock.lock();
 40         try {
 41             //判断
 42             while (num != 1) {
 43                 condition1.await();
 44             }
 45             //干活
 46             for (int i = 1; i <= 5; i++) {
 47                 System.out.println(Thread.currentThread().getName() + "	" + i);
 48             }
 49             //通知
 50             num = 2;//修改标志位
 51             condition2.signal();//通知第二把钥匙condition2
 52 
 53         } catch (Exception e) {
 54             e.printStackTrace();
 55         } finally {
 56             lock.unlock();
 57         }
 58     }
 59 
 60     public void print10() {
 61         lock.lock();
 62         try {
 63             //判断
 64             while (num != 2) {
 65                 condition2.await();
 66             }
 67             //干活
 68             for (int i = 1; i <= 10; i++) {
 69                 System.out.println(Thread.currentThread().getName() + "	" + i);
 70             }
 71             //通知
 72             num = 3;//修改标志位
 73             condition3.signal();//通知第三把钥匙condition3
 74 
 75         } catch (Exception e) {
 76             e.printStackTrace();
 77         } finally {
 78             lock.unlock();
 79         }
 80     }
 81 
 82     public void print15() {
 83         lock.lock();
 84         try {
 85             //判断
 86             while (num != 3) {
 87                 condition3.await();
 88             }
 89             //干活
 90             for (int i = 1; i <= 15; i++) {
 91                 System.out.println(Thread.currentThread().getName() + "	" + i);
 92             }
 93             //通知
 94             num = 1;//修改标志位
 95             condition1.signal();//通知第一把钥匙condition1
 96         } catch (Exception e) {
 97             e.printStackTrace();
 98         } finally {
 99             lock.unlock();
100         }
101     }
102 
103 }
104 
105 结果打印:
106 A    1
107 A    2
108 A    3
109 A    4
110 A    5
111 B    1
112 B    2
113 B    3
114 B    4
115 B    5
116 B    6
117 B    7
118 B    8
119 B    9
120 B    10
121 C    1
122 C    2
123 C    3
124 C    4
125 C    5
126 C    6
127 C    7
128 C    8
129 C    9
130 C    10
131 C    11
132 C    12
133 C    13
134 C    14
135 C    15

 4、多线程锁机制

A 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法

加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化。


synchronized实现同步的基础:Java中的每一个对象都可以作为锁。
具体表现为以下3种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象

当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。

也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,
可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,
所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。

所有的静态同步方法用的也是同一把锁——类对象本身,
这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,
而不管是同一个实例对象的静态同步方法之间,
还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!

原文地址:https://www.cnblogs.com/zsy-code/p/13696247.html