生产者消费者模型
模型描述:通常有两类线程,有若干个生产者线程和若干个消费者线程,生产者线程负责生产数据,消费者线程负责消费数据,在生产者和消费者之间设置缓存区域进行线程之间的通讯。
解决问题:由于缓冲区的存在,可以使生产者和消费者之间进行解耦,解决了生产者和消费者之间消息生产和消费不平衡的问题,实现线程之间的互相通信。
存在角色:
生产者:负责消息的生产。
消费者:负责消费生产者产生的消息。
缓存区:负责存放消息,生产者将消息放入,消费者将消息拿出。
模型举例
举一个送牛奶的例子,我在屋里写代码,送奶工人将送来的牛奶放入奶箱中,等我空闲的时候我就去奶箱中把牛奶拿出来,某一天,我由于加班,忘记把牛奶拿出来了,当送奶工把牛奶送来的时候,奶箱放不下了,此时,送奶工人就给我打电话,让我赶紧把牛奶拿走。过了些日子,我发现屋里没有牛奶了,我就去门口的奶箱中拿,此时奶箱中也没有牛奶了,然后我就给送奶工打电话,让他赶紧把牛奶送来。
例子中的送牛奶的工人就是生产者,牛奶指的是产生的消息,奶箱代表缓冲区,我代表消费者,送奶工打电话通知我拿走牛奶和我打电话通知送奶工送牛奶过来,代表着生产者和消费者之间的通信。
具体实现
创建牛奶类(产生的消息)
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瓶牛奶
...
...
*/