學習線程通訊以前咱們須要簡單的瞭解下生產者和消費者模式。
而後咱們經過生產者和消費者 學習到線程間通訊 等待與喚醒機制。java
生產者
就是生產東西 如生產商品消費者
就是消費東西 如 賣商品微信
System.out.println("生產1"); System.out.println("消費1");
package com.company.threadcommunication; /** * @description: 線程通訊之 生產者 消費者 * @author: tizzy * @create: 2019-12-18 20:59 **/ public class ThreadCommunication1 { public static void main(String[] args) { Communication1 communication = new Communication1(); new Thread(){ @Override public void run() { communication.produce(); } }.start(); new Thread(){ @Override public void run() { communication.consumer(); } }.start(); } } class Communication1{ int i = 0; /** * 生產者 */ public void produce(){ while (true){ i++; System.out.println(" 生產者 ---> " + i); } } /** * 消費者 */ public void consumer(){ while (true){ System.out.println(" 消費者 ---> " + i); } } }
啓動運行生產者線程和消費者線程後發現 生產者和消費者並非按照咱們的意願在生產和消費。
能夠看到生產者一直在生產,消費者也一直在消費,可是消費者存在重複消費的狀況。
咱們指望看到生產者生產一個,而後消費者消費一個這樣子的運行結果。
可是爲何會出現下面這種狀況,緣由在於,生產者一直只顧着本身生產,消費者只顧着本身消費,無論有沒有被消費過。多線程
如何讓兩個線程生產一個而後在立刻消費一個?咱們可讓生產者線程和消費者線程互相通訊,當生產者生產一個以後,等着別生產,而後告訴消費者你該消費了,消費者消費完了,先等着別消費了,告訴生產者你趕忙生產 ........ 一直這樣子互相告訴對方信息,放到線程裏咱們就叫作 線程通訊
ide
咱們來修改上面的代碼
這裏須要用到兩個方法學習
wait()
讓當前線程等着,釋放cpu執行權,在當前對象鎖中的線程池中排隊,將線程臨時存儲到了線程池中。
當前線程必須擁有此對象的監視器(鎖),不然拋出java.lang.IllegalMonitorStateException
this
notify()
喚醒等待的一個線程,讓其餘線程去執行調度spa
notifyAll()
: 會喚醒線程池中全部的等待的線程。線程
這些方法必須使用在同步中,由於必需要標識wait、notify等方法所屬的鎖。同一個鎖上的notify,只能喚醒改鎖上wait的線程。默認是this.wait();this.notify();this.notifyAll()。3d
爲何這些方法定義在Object類中,而不是Thread類中呢?
code
由於這些方法必須標識所屬的鎖,而鎖能夠是任意對象,任意對象能夠調用的方法必然是Object類中的方法。
package com.company.threadcommunication; /** * @description: 線程通訊 * @author: tizzy * @create: 2019-12-18 20:59 **/ public class ThreadCommunication2 { public static void main(String[] args) { Communication2 communication = new Communication2(); new Thread() { @Override public void run() { while (true){ communication.produce(); } } }.start(); new Thread() { @Override public void run() { while (true) { communication.consumer(); } } }.start(); } } class Communication2 { //對象 private final Object object = new Object(); //是否生產 volatile boolean falg = false; //沒有生產 int i = 0; /** * 生產者 */ public void produce() { synchronized (object) { //true 生產 if (!falg) { i++; System.out.println(" 生產 : " + i); //喚醒 消費者去消費 object.notify(); falg= true; } else { //生產了就等着,不在生產,等待消費者去消費 try { object.wait(); //wait 當前線程處於等待狀態 而且釋放執行權,釋放鎖 } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 消費者 */ public void consumer() { synchronized (object){ //消費 if(falg){ System.out.println(" 消費 : " + i); //通知去生產 object.notify(); falg= false; }else { try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
運行結果
能夠看到,生產者每次生產一個,而後消費者消費一個。
須要注意的是
多線程下就不能使用notify()來喚醒線程了,必須使用notifyAll()來喚醒全部等待的線程。
package com.company.threadcommunication; import java.util.stream.Stream; /** * @description: 線程通訊 * @author: Administrator * @create: 2019-12-18 20:59 **/ public class ManyThreadManyCommunication3 { public static void main(String[] args) { Communication3 communication = new Communication3(); //多個生產者 Stream.of("p1", "p2", "p3").forEach(s -> { new Thread() { @Override public void run() { while (true) { communication.produce(s); } } }.start(); }); //多個消費者 Stream.of("c1", "c2", "c3").forEach(s -> { new Thread() { @Override public void run() { while (true) { communication.consumer(s); } } }.start(); }); } } class Communication3 { //對象 private final Object object = new Object(); //是否生產 volatile boolean falg = false; //沒有生產 int i = 0; /** * 生產者 */ public void produce(String name) { synchronized (object) { //true 生產 while (!falg) { i++; System.out.println(name + " 生產 : " + i); //喚醒 消費者去消費 object.notifyAll(); falg = true; } //生產了就等着,不在生產,等待消費者去消費 try { object.wait(); //wait 等待線程 釋放鎖 } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 消費者 */ public void consumer(String name) { synchronized (object) { //消費 while (falg) { System.out.println( name + " 消費 : " + i); //通知去生產 object.notifyAll(); falg = false; } try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
能夠看到結果
注意
生產者和消費者 這裏使用while去判斷是否須要生產和消費標識
//消費 while (falg) { ..... } //生產 while (!falg) { ..... }
須要注意的是:
當多個線程這行到這裏時候若是沒有使用while
而用if
判斷 falg
執行標識,會存在重複生產或消費的狀況。
這裏假設消費者先搶到cpu執行權,falg 是false 消費者處於等待狀態,而後生產者第一個線程進來後發現已經非falg 是true 是須要生產,而後進行生產,此時falg設爲false ,第二個生產者線程再進來,if
中條件已經爲false 不會去喚醒消費者去消費,直接進行生產數據,這樣子重複生產,同理狀況下消費者亦是如此。
因此這裏須要用到while
去循環判斷狀態標識,避免重複消費或生產。
更多請關注微信公衆號