多线程之线程间通讯 ,生产者和消费者,等待和唤醒机制

多线程之线程间通讯

学习线程通讯以前咱们须要简单的了解下生产者和消费者模式。
而后咱们经过生产者和消费者 学习到线程间通讯 等待与唤醒机制。java

1 生产者和消费者

  • 先来看生产者和消费者

生产者 就是生产东西 如生产商品
消费者 就是消费东西 如 卖商品微信

  • 用代码来描述就是:
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

2 单线程下的线程通讯

咱们来修改上面的代码
这里须要用到两个方法学习

线程等待

wait() 让当前线程等着,释放cpu执行权,在当前对象锁中的线程池中排队,将线程临时存储到了线程池中。
当前线程必须拥有此对象的监视器(锁),不然抛出java.lang.IllegalMonitorStateExceptionthis

线程唤醒

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();
                }
            }
        }
    }
}

运行结果

在这里插入图片描述
能够看到,生产者每次生产一个,而后消费者消费一个。

须要注意的是

  • 1 wait()、notify()、notifyAll()这些方法必须使用在同步中,由于它们是用来操做同步锁上的线程的状态的;
  • 2 同时在使用这些方法时,必须标识它们所属于的锁,标识方式就是:锁对象.wait()、锁对象.notify()、锁对 象.notifyAll()。相同锁的notify(),能够获取相同锁的wait();
  • 3 锁能够是任意对象,因此任意对象调用的方法必定定义在Object类中。

3 多线程下生产者消费者

多线程下就不能使用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去循环判断状态标识,避免重复消费或生产。

更多请关注微信公众号

公众号二维码.png

相关文章
相关标签/搜索