生产者消费者模型

模型描述:通常有两类线程,有若干个生产者线程和若干个消费者线程,生产者线程负责生产数据,消费者线程负责消费数据,在生产者和消费者之间设置缓存区域进行线程之间的通讯。

解决问题:由于缓冲区的存在,可以使生产者和消费者之间进行解耦,解决了生产者和消费者之间消息生产和消费不平衡的问题,实现线程之间的互相通信。

存在角色

生产者:负责消息的生产。

消费者:负责消费生产者产生的消息。

缓存区:负责存放消息,生产者将消息放入,消费者将消息拿出。

模型举例

举一个送牛奶的例子,我在屋里写代码,送奶工人将送来的牛奶放入奶箱中,等我空闲的时候我就去奶箱中把牛奶拿出来,某一天,我由于加班,忘记把牛奶拿出来了,当送奶工把牛奶送来的时候,奶箱放不下了,此时,送奶工人就给我打电话,让我赶紧把牛奶拿走。过了些日子,我发现屋里没有牛奶了,我就去门口的奶箱中拿,此时奶箱中也没有牛奶了,然后我就给送奶工打电话,让他赶紧把牛奶送来。

例子中的送牛奶的工人就是生产者,牛奶指的是产生的消息,奶箱代表缓冲区,我代表消费者,送奶工打电话通知我拿走牛奶和我打电话通知送奶工送牛奶过来,代表着生产者和消费者之间的通信。

具体实现

创建牛奶类(产生的消息)

package com.sj.pcmodel;
//定义一个牛奶类
public class Milk {
    private int id;

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

创建奶箱类(缓冲区),提供添加和获取牛奶的方法

package com.sj.pcmodel;

//奶箱类
public class MilkBox {
    //定义了一个长度为5的数组当做奶箱,最多放5瓶奶;
    private   Milk milksBox [] = new Milk[5];
    //定义一个变量,表示奶箱中当前奶的数量
    private int count=0;
   //定义一个添加牛奶的方法,模拟送奶工送牛奶
   public synchronized void addMilk(Milk milk){
       //当奶箱满的时候,此时送奶工人休息,不再送奶
       //奶箱长度减一是因为count从0开始,所以当count等于4时,奶箱中就有5瓶奶了
       while (count==milksBox.length-1){
           try {
               wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
       //唤醒消费者线程,也就是提醒我,奶箱中有牛奶该消费了
            notifyAll();
       //往奶箱中添加牛奶
         milksBox [count]=milk;
            count++;


   }


   //定义一个获取牛奶的方法,用于消费牛奶
   public synchronized Milk getMilk(){
       //如果奶箱中没有牛奶了,我就休息,不去消费
        while (count==0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        //唤醒生产者线程,也就是提醒送奶工人,该给我送牛奶了
       notifyAll();
        //消费牛奶
        count--;
        //返回被消费的牛奶,便于打印数据
        return milksBox [count];
   }

}

创建生产者类(送奶工人),实现runnable接口,在run方法中,循环调用添加牛奶的方法

package com.sj.pcmodel;

//定义一个生产者类,实现runnable,把奶箱对象作为构造方法参数
public class Productor implements Runnable {
    private MilkBox milkBox;

    public Productor(MilkBox milkBox) {
        this.milkBox = milkBox;
    }
    //在run方法中,调用添加牛奶的方法,循环添加
    @Override
    public void run() {
        for (int i = 1; i <21; i++) {
            System.out.println("送来了第"+i+"瓶牛奶");
            milkBox.addMilk(new Milk(i));
        }
    }
}

创建消费者类(我拿牛奶),实现runnable接口,在run方法中,循环调用获取牛奶的方法

package com.sj.pcmodel;

//定义一个消费者类,实现runnable,把奶箱对象作为构造方法参数
public class Consumer implements Runnable {
    private MilkBox milkBox;

    public Consumer(MilkBox milkBox) {
        this.milkBox = milkBox;
    }
//在run方法中调用消费牛奶的方法,循环调用
    @Override
    public void run() {
        for (int i = 1; i <21; i++) {
            System.out.println("我拿走了第"+ milkBox.getMilk().getId()+"瓶牛奶");
        }
    }
}

测试

package com.sj.pcmodel;

//测试结果
public class PcTest {

    public static void main(String[] args) {
        //创建奶箱对象,
       MilkBox milkBox = new MilkBox();
       //创建生产者,消费者对象
       Consumer consumer = new Consumer(milkBox);
       Productor productor = new Productor(milkBox);
       //启动线程
       new Thread(productor).start();
       new Thread(consumer).start();
    }
}
/*
	输出结果:
	送来了第1瓶牛奶
    送来了第2瓶牛奶
    我拿走了第1瓶牛奶
    我拿走了第2瓶牛奶
    送来了第3瓶牛奶
    送来了第4瓶牛奶
    我拿走了第3瓶牛奶
    送来了第5瓶牛奶
    我拿走了第4瓶牛奶
    送来了第6瓶牛奶
    我拿走了第5瓶牛奶
    送来了第7瓶牛奶
    我拿走了第6瓶牛奶
    送来了第8瓶牛奶
    我拿走了第7瓶牛奶
    送来了第9瓶牛奶
    我拿走了第8瓶牛奶
    送来了第10瓶牛奶
    我拿走了第9瓶牛奶
		...
		...
*/