线程通信(生产者和消费者)

wuchangjian2021-11-16 19:19:28编程学习

1.分析

这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。

  • 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,有需要马上通知消费者消费
  • 对于消费者,消费完之后,要通知生产者已经结束消费,需要生产新的产品以供消费
  • 生产者消费者问题中,仅有synchronized是不够的
    • synchronized可阻止并发更新同一个共享资源,实现了同步
    • synchronized不能用来实现不同线程之纪检的消息传递

2.线程通信提供的方法

方法名作用
wait()表示一直等待,知道其他线程进行通知,与sleep(抱着锁睡着)不同,会释放锁(不占用资源)。
wait(long timeout)指定等待的毫秒数。
notify()唤醒一个等待状态的线程(随机唤醒)
notifyAll()唤醒一个对象中上所有被调用wait()方法的线程,优先级高的,优先被调度。

注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IIIegalMonitorStateException

3.解决方法1

并发协作模型“生产者/消费者模式”--->管程法

  • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程);
  • 消费者:负责处理数据的模块(可能是方法,对象,线程,进程);
  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区

生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

package com.thread.senior;

/**
 * @description: 测试生产者消费者----->管程法
 * @author:wt
 * @createTime:2021/11/15 15:27
 * @version:1.0
 */
public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        Producer producer = new Producer(synContainer);
        Consumer consumer = new Consumer(synContainer);
        producer.start();
        consumer.start();
    }
}

class Producer extends Thread{
    //需要容器
    SynContainer container;
    public Producer(SynContainer container){
        this.container = container;
    }
    // 进行生产鸡
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println("生产了第"+ i + "只鸡!");
            container.Product(new Chicken(i));
        }
    }
}

class Consumer extends Thread{
    //需要容器
    SynContainer container;
    public Consumer(SynContainer container){
        this.container = container;
    }
    // 进行消费鸡
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println("消费者吃了第"+ container.consume().id + "只鸡" );
        }
    }
}

class Chicken {
    int id ; // 鸡的编号
    public Chicken(int id){
        this.id = id ;
    }
}

class SynContainer {
    // 缓冲区的容器
    Chicken[] chickens = new Chicken[10];
    // 容器计数器
    int count = 0;
    //生产者放入产品
    public synchronized void Product(Chicken chicken){
        // 如果容器满了,生产者需要等待消费者消费
        if(count == chickens.length) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        chickens[count] = chicken;
        count ++;

        // 缓冲区里面有鸡了,开始通知消费者消费了
        this.notifyAll();
    }

    public synchronized Chicken consume(){
        // 如果容器是空的,消费者不能进行消费,需要等到生产者生产鸡
        if(count == 0 ) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //开始吃鸡了,取得是最后一只,并进行保存!
        count -- ;
        Chicken eatChicken = chickens[count];

        // 我已经把鸡吃了,开始通知生产者继续生产了

        this.notifyAll();
        return eatChicken;
    }
}

4.解决方法2

并发协作模型“生产者/消费者模式”--->信号灯法

给出一个标志,如果为真给一个true,如果为假给一个false。

package com.thread.senior;

/**
 * @description: 测试生产者消费者----->信号灯法
 * @author:wt
 * @createTime:2021/11/15 18:25
 * @version:1.0
 */
public class TestSignal {
    public static void main(String[] args) {
        Cctv cctv = new Cctv();
        new Palyer(cctv).start();
        new Viewer(cctv).start();
    }
}

class Palyer extends Thread{
    // 需要cctv平台表演
    Cctv cctv;
    public Palyer( Cctv cctv) {
        this.cctv=cctv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i%2==0){
                cctv.perform("表演今平没");
            }else {
                cctv.perform("拍抖音短视频");
            }
        }
    }
}

class Viewer extends Thread{
    // 需要cctv平台表演
    Cctv cctv;
    public Viewer( Cctv cctv) {
        this.cctv=cctv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("观众正在观看"+ cctv.view());
        }
    }
}

// 相当于一个信号塔,通过标识来通知生产者消费者
class Cctv {
    // 演员节目
    String item ;
    // 信号标识
    boolean flag = true;
    //表演节目
    public synchronized void perform (String item ){
        //是否需要表演
        if (!flag){
            // 请等待观众看完上场表演
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.flag= !this.flag;
        // 开始表演节目
        this.item=item;
        System.out.println("演员"+ item);
        //通知
        this.notifyAll();
    }
    //观看节目
    public synchronized String view (){
        //是否有观看的表演
        if (flag){
            //正在表演,请等待观看
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.flag= !this.flag;
        this.notifyAll();
        return this.item;
    }
}


5.应用场景:生产者和消费者问题

  • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。
  • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产者等待,直到仓库的产品被消费者取走为止。
  • 如果仓库中没有产品,则消费者将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。