问题描述
生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。生产者生成一定量的数据放到缓冲区中,然后重复此过程;与此同时,消费者也在缓冲区消耗这些数据。生产者和消费者之间必须保持同步,要保证生产者不会在缓冲区满时放入数据,消费者也不会在缓冲区空时消耗数据。不够完善的解决方法容易出现死锁的情况,此时进程都在等待唤醒。
先举个例子:
卖家:汽车厂商 买家:张三
张三想买一辆五菱宏光 , 告知汽车厂商我要买车。张三会进入等待状态,等到五菱厂家造完以后,再通知张启来提车。如果五菱厂家有现车,张启就直接提车。
如果产品需要生产的话,消费者进入到阻塞状态。
如果产品不需要生产的话,消费者直接购买。
解决方法
采用某种机制保护生产者和消费者之间的同步。有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。
在生产者和消费者之间建立一个管道。管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。
保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。
图解:
代码实现:
仓库类
//为啥要写这个类?这个类是作为两个线程之间通信的桥梁
class Goods {
private String name;//名字
private double price;//价格
private boolean isProduct;//是否有这个商品, true 需要生产
//false 不需要生产商品
public Goods(String name, double price, boolean isProduct) {
this.name = name;
this.price = price;
this.isProduct = isProduct;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public boolean isProduct() {
return isProduct;
}
public void setProduct(boolean product) {
isProduct = product;
}
}
消费者
//消费者线程
class Customer implements Runnable {
private Goods goods;
public Customer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
synchronized (goods) {
//一直消费
//goods.isProduct() true 需要生产 没有商品
//false 不需要生产的
if (!goods.isProduct()) {
System.out.println("消费者购买了:" + goods.getName() + ",价格为:" + goods.getPrice());
//购买完以后 商品没了 isProduct 是true 没有商品了
goods.setProduct(true);
//唤醒生产者去生产
goods.notify();
} else {
//需要生产 消费者进入阻塞
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
生产者
//生产者线程
class Productor implements Runnable {
private Goods goods;
public Productor(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
int count = 0 ;
while (true) {//一直生产商品
synchronized (goods) {
if (goods.isProduct()) {//true 需要生产者
//造车,奇数造一种车, 偶数的话造另外一种车
if (count % 2 == 0) {//偶数
goods.setName("玛莎拉蒂");
goods.setPrice(200);
} else {
goods.setName("五菱宏光");
goods.setPrice(400);
}
//生产完以后一定要记得 将标记改false
goods.setProduct(false);
System.out.println("生产者生产了:" + goods.getName() + ",价格为:" + goods.getPrice());
count++;
//生产者完了以后,人家消费者在等待这你呢
//唤醒消费者
goods.notify();
} else {
//不需要生产车,你就等着就行
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
测试类
public class Demo2 {
public static void main(String[] args) {
Goods goods = new Goods("东风", 89, false);
//false不需要生产的 谁阻塞 生产者阻塞
//最好是让生产者线程先执行
Productor productor = new Productor(goods);
new Thread(productor).start();
Customer customer = new Customer(goods);
new Thread(customer).start();
/**
* 消费者购买了:东风,价格为:89.0
* 生产者生产了:玛莎拉蒂,价格为:200.0
* 消费者购买了:玛莎拉蒂,价格为:200.0
* 生产者生产了:五菱宏光,价格为:400.0
* 消费者购买了:五菱宏光,价格为:400.0
* 生产者生产了:玛莎拉蒂,价格为:200.0
* isProduct = false
* 走了else 代码是生产者阻塞了 消费在睡了一秒之后立马执行
* 看消费者 有商品 买走了,之后 isProduct=true
* 唤醒生产者了,
* 生产者生产了:玛莎拉蒂,价格为:200.0 isProduct=false
*
*/
}
}