Java学习——生产者-消费者模式与线程通信问题(管程法、信号灯法)

Java学习——生产者消费者模式与线程通信问题(管程法)

什么是生产者消费者模式?

生产者消费者模式并不是23中设计模式中的一种,在面向对象编程中我们前人总结了一些比较好的设计模式,就像23种设计模式一样。但是面向过程编程的时候,我们前人也给我们总结了一些好的设计模式,比如今天我们要了解到的生产者-消费者模式。

在我们日常升生活中,常常会见到这样一种场景,比如我们取麦当劳买东西,当我们点完之后,会在一旁等待着我们的餐,然后做好了服务员就会通知我们取拿餐,这样一种简单的模式就是典型的生产者-消费者模式。

在我们多线程中要实现线程之间的通信(线程协作),就需要用到生产者-消费者模式
解决线程通信有两种方式:1.管程法 2.信号灯法

1.管程法
今天我们重点学习一下管程法,管程法主要以下几个要素:

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

我们来看下代码实现:

/**
 * 线程通信-管程法
 */
public class ThreadPcOfTubeMethod {
    public static void main(String[] args) {
        SynchronizeContainer container = new SynchronizeContainer();
        new Product(container).start();
        new Consumers(container).start();
    }
}

//1.生产者
class Product extends Thread{
    SynchronizeContainer container;
    public Product(SynchronizeContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        //生产
        for (int i = 0; i < 100; i++) {
            System.out.println("生产----->第"+i+"个面包");
            container.push(new Bread(i));
        }
    }
}

//2.消费者
class Consumers extends Thread{
    SynchronizeContainer container;
    public Consumers(SynchronizeContainer container) {
        this.container = container;
    }
    @Override
    public void run() {
        //消费
        for (int i = 0; i < 100; i++) {
            System.out.println("消费----->第"+container.pop().id+"个面包");
        }
    }
}


//3.并发容器(缓冲区):
//      1.存:容器不够了,不能存了,等待消费
//      2.取:容器为空容器,等待生产
class SynchronizeContainer{
    Bread[] breads = new Bread[10];
    int count = 0;//计数器
    
    //生产----存储
    public synchronized void  push(Bread bread){
        //容器存在空间可以生产
        //不能生产
        if(count==breads.length){
            try {
                this.wait();//线程阻塞,消费者通知生产,阻塞解除
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        breads[count] = bread;
        count++;

        this.notifyAll();//存在数据了,通知消费
    }

    //消费----获取
    public synchronized Bread pop(){
        //如果容器中有数据,可以消费。

        //如果没有数据,等待
        if (count==0){
            try {
                this.wait();//线程阻塞,生产者通知消费解除阻塞
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


        //从最后一个位置拿
        count--;
        Bread bread =breads[count];

        this.notifyAll();//存在空间了,可以唤醒生产者生产了
        return bread;
    }
}

//4.商品(面包)
class Bread{
    int id;

    public Bread(int id) {
        this.id = id;
    }
}

2.信号灯法
我们简单的了解一下信号灯法,信号灯法是通过标志位来实现的。举一个演员表演,观众看的例子:

/**
 * 协作模型:生产者消费者实现方式二-信号灯法
 * 思想:借助标志位
 */
public class ThreadPcOfSignalLight {
    public static void main(String[] args) {
        Tv tv = new Tv();
        new Players(tv).start();
        new Watchers(tv).start();
    }
}

//生产者:演员
class Players extends Thread{
    Tv tv;
    public Players(Tv tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                this.tv.play("天龙八部");
            }else {
                this.tv.play("广告:天才第一步,雀氏纸尿裤");
            }
        }
    }
}

//消费者:观众
class Watchers extends Thread{
    Tv tv;
    public Watchers(Tv tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}


//同一个资源:电视
class Tv{
    String voice;
    //信号灯,如果为真,表示观众等待
    //信号灯,如果为假,表示演员等待
    boolean flag = true;

    //表演
    public synchronized void play(String voice){
        //演员等待
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("演员表演了:"+voice);
        this.voice=voice;
        //唤醒
        this.notifyAll();
        //切换信号灯的状态
        this.flag=!this.flag;

    }

    //观看
    public synchronized void watch(){
        //观众等待
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众听到了:"+voice);
        //唤醒
        this.notifyAll();
        //切换信号灯的状态
        this.flag=!this.flag;
    }
}

你可能感兴趣的:(Java基础)