黑马程序员技术交流社区
标题:
生产者消费者模型
[打印本页]
作者:
psuugdufnm
时间:
2016-4-12 13:28
标题:
生产者消费者模型
一、介绍
:
(一)、概念:
如果要实现以下操作流程,使用Java代码来实现:
多个蛋糕师生产蛋糕,多个消费者购买蛋糕;
蛋糕的最大库存为5个;
早生产的蛋糕先被销售,最后被生产的蛋糕要最后被售出
如果要实现这个过程,一定要借助Java线程的并发协作来做。其实这在Java中叫做
生产者消费者模型
(确切说应该是
“
生产者-消费者-仓储
”模型
)。
对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的。就像学习每一门编程语言一样,Hello World!都是最经典的例子。
(二)、 生产者消费者模型,应该明确一下几点:
1、生产者仅仅在仓储未满时候生产,
仓满
则停止生产。
2、消费者仅仅在仓储有产品时候才能消费,
仓空
则等待。
3、当消费者发现仓储没产品可消费时候会
通知生产者生产
。
4、生产者在生产出可消费产品时候,应该
通知等待的消费者去消费
。
(三)、核心技术:
Java中提供了三个非常重要方法用来解决“
生产者-消费者-仓储
”模型 。
wait
():让当前线程进入阻塞状态,将这个线程存储到等待池中,并释放当前线程的所获得的锁
notify
():唤醒等待池中的一个线程(
随机
)
notifyAll
():唤醒等待池中所有的线程
【注意】
wait、notify、notifyAll三个方法必须被在
同步代码中被调用
这些方法都是用于操作线程状态,那么就必须要明确到底操作的是哪个锁上的线程
作者:
strongmanzxh
时间:
2016-4-12 15:53
看不懂呀。。。。
作者:
zhoubinjian
时间:
2016-4-12 16:32
程序呢,,让我们看看啊
作者:
(り____花子
时间:
2016-4-12 16:32
现在没人用同步了。你试试用lock 、condition 更简单。
作者:
psuugdufnm
时间:
2016-4-12 20:40
二、代码实现
(一)、实现步骤:
1、定义仓库类:
实现生产和消费方法
在仓满和仓空时等待(wait)和唤醒(notify)
使用同步锁
2、定义生产者线程类
定义构造方法,初始化仓库类
在run方法中无限循环执行仓库类的生产方法
3、定义消费者线程类
定义构造方法,初始化仓库类
在run方法中无限循环执行仓库类的消费方法
(二)、核心代码
public class ProducerConsumerGodown {
public static void main(String[] args) throws Exception {
new ProducerConsumerGodown().startTest();
}
public void startTest() {
Godown godown = new Godown();
Producer producer1 = new Producer(godown);
producer1.setName("1号糕点师:");
producer1.start();
Producer producer2 = new Producer(godown);
producer2.setName("2号糕点师:");
producer2.start();
Producer producer3 = new Producer(godown);
producer3.setName("3号糕点师:");
producer3.start();
Producer producer4 = new Producer(godown);
producer4.setName("4号糕点师:");
producer4.start();
Producer producer5 = new Producer(godown);
producer5.setName("5号糕点师:");
producer5.start();
Consumer consumer1 = new Consumer(godown);
consumer1.setName("张三:");
consumer1.start();
Consumer consumer2 = new Consumer(godown);
consumer2.setName("李四:");
consumer2.start();
Consumer consumer3 = new Consumer(godown);
consumer3.setName("王五:");
consumer3.start();
}
class Godown {
// 定义柜台中的蛋糕
private LinkedList<Object> storeHouse = new LinkedList<Object>();
// 定义柜台中最大允许的蛋糕库存量
private int MAX = 5;
private Object lock = new Object();
// 生产蛋糕
public void produce() {
try {
synchronized (lock) {
// if(storeHouse.size() >=
// MAX)//注意这里只能用while循环不能用if来判断,if判断只适合1对1的关系,就是只有2个线程,
// 如果多个线程用if判断的话,就会出现逻辑错误。
// 所以在多线程中都用while来做判断,不用if
while (storeHouse.size() == MAX) {
System.out.println("蛋糕库存已满,糕点师停止生产,等待售出!---库存量:"
+ storeHouse.size());
lock.wait();
}
Object newObj = new Object();
// 往柜台中添加蛋糕
if (storeHouse.add(newObj)) {
System.out.println(Thread.currentThread().getName()
+ "生产入库!---产品编号:" + newObj.hashCode()
+ "---库存 增加 至:" + storeHouse.size());
// 唤醒在此对象锁上处于等待状态的所有线程
lock.notifyAll();
}
}
Thread.sleep((long) (Math.random() * 3000));
} catch (InterruptedException e) {
System.out.println(e.toString());
}
}
public void consume() {
try {
synchronized (lock) {
// 在多线程中都用while来做判断,不用if
while (storeHouse.size() == 0) {
System.out.println("蛋糕已售空,请消费者等待!---库存量:"
+ storeHouse.size());
lock.wait();
}
// 将集合中的第一个元素移除,也就是将最早生产的蛋糕售出
// LinkedList与ArrayList相比,后者不具有removeFirst方法
Object object = storeHouse.removeFirst();
System.out.println(Thread.currentThread().getName()
+ "购买产品!---产品编号:" + object.hashCode()
+ "---库存 减少 至:" + storeHouse.size());
// 唤醒在此对象锁上处于等待状态的所有线程
lock.notifyAll();
}
Thread.sleep((long) (Math.random() * 3000));
} catch (InterruptedException e) {
System.out.println(e.toString());
}
}
}
class Producer extends Thread {
private Godown godown;
public Producer(Godown godown) {
this.godown = godown;
}
public void run() {
while (true) {
// 生产
godown.produce();
}
}
}
// 定义消费者类
class Consumer extends Thread {
private Godown godown;
public Consumer(Godown godown) {
this.godown = godown;
}
public void run() {
while (true) {
// 消费
godown.consume();
}
}
}
}
【附加:】
1、ArrayList 查找速度快,原因是ArrayList的底层用得是数组结构;插入和删除慢,因为需要移动后续的对象;
2、LinkedList 查找速度慢,原因是迭代循环;插入和删除快,原因是只更新引用。
作者:
白龙马大魔王
时间:
2016-4-12 20:53
作者:
psuugdufnm
时间:
2016-4-15 22:58
每天积累分。
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2