学习线程通讯以前咱们须要简单的了解下生产者和消费者模式。
而后咱们经过生产者和消费者 学习到线程间通讯 等待与唤醒机制。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
去循环判断状态标识,避免重复消费或生产。
更多请关注微信公众号