前言
学完了线程后,我又去找了一些线程相关的练习题来练手,其中印象最深的就是生产者消费者模型这一块,为什么呢,因为它每一篇练习题里都有,开始没看懂,后面就去仔细研究了一下,哦,原来是这样,好像是懂了,那就来写写博客吧。哈哈哈……
什么是生产者消费者模型
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
简单来说:
生产者消费者模型就是指,在一个系统中,存在两种角色,一个为生产者,一个为消费者,通过一个缓冲区(仓库)进行通信,生产者将生产的产品放入仓库,消费者从仓库中取产品。当仓库满时,生产者阻塞,当仓库空时,消费者阻塞。
关系图
实现方式
- 采用 wait—notify 的方式
- 采用 阻塞队列 方式
当然了,还有其他的实现方式,这里就不一一列举了。
wait—notify 方式
接下来,我就直接上代码了。
会涉及四个类,每一个类里都有注释讲解。
/**
* 生产者类
* 实现runnable接口
* @author DH
*
*/
public class Producer implements Runnable{
private BufferArea ba;
//通过传入参数的方式是使得对象相同,具有互斥锁的效果。
public Producer(BufferArea ba){
this.ba = ba;
}
@Override
public void run() {
while(true){
setIntervalTime();
ba.set();//生产产品
}
}
//设置时间间隔
public void setIntervalTime(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 消费者类
* 实现runnable接口
* @author DH
*
*/
public class Consumer implements Runnable{
private BufferArea ba;
public Consumer(BufferArea ba){
this.ba = ba;
}
@Override
public void run() {
while(true){
setIntervalTime();
ba.get();//消费产品
}
}
//设置时间间隔
public void setIntervalTime(){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 仓库
* 缓冲区
* wait()/notify()
* @author DH
*
*/
public class BufferArea {
private int currNum = 0;//当前仓库的产品数量
private int maxNum = 10;//仓库最大产品容量
public synchronized void set(){
if(currNum<maxNum){
currNum++;
System.out.println(Thread.currentThread().getName()+" 生产了一件产品!当前产品数为:"+currNum);
notifyAll();
}else{//当前产品数大于仓库的最大容量
try {
System.out.println(Thread.currentThread().getName()+" 开始等待!当前仓库已满,产品数为:"+currNum);
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get(){
if(currNum>0){//仓库中有产品
currNum--;
System.out.println(Thread.currentThread().getName()+" 获得了一件产品!当前产品数为:"+currNum);
notifyAll();
}else{
try {
System.out.println(Thread.currentThread().getName()+" 开始等待!当前仓库为空,产品数为:"+currNum);
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 测试类
* @author DH
*
*/
public class MainCode {
public static void main(String[] args) {
//同一个仓库
BufferArea ba = new BufferArea();
//三个生产者
Producer p1 = new Producer(ba);
Producer p2 = new Producer(ba);
Producer p3 = new Producer(ba);
//三个消费者
Consumer c1 = new Consumer(ba);
Consumer c2 = new Consumer(ba);
Consumer c3 = new Consumer(ba);
//创建线程,并给线程命名
Thread t1 = new Thread(p1,"生产者1");
Thread t2 = new Thread(p2,"生产者2");
Thread t3 = new Thread(p3,"生产者3");
Thread t4 = new Thread(c1,"消费者1");
Thread t5 = new Thread(c2,"消费者2");
Thread t6 = new Thread(c3,"消费者3");
//使线程进入就绪状态
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
通过设置生产者消费者的时间间隔,可以测试仓库满和空时的情况,当生产者时间间隔小,表示生产的快,会出现仓库满了的情况。测试结果如下。
生产者2 生产了一件产品!当前产品数为:1
生产者1 生产了一件产品!当前产品数为:2
生产者3 生产了一件产品!当前产品数为:3
生产者3 生产了一件产品!当前产品数为:4
生产者1 生产了一件产品!当前产品数为:5
生产者2 生产了一件产品!当前产品数为:6
生产者1 生产了一件产品!当前产品数为:7
生产者3 生产了一件产品!当前产品数为:8
生产者2 生产了一件产品!当前产品数为:9
生产者3 生产了一件产品!当前产品数为:10
生产者2 开始等待!当前仓库已满,产品数为:10
生产者1 开始等待!当前仓库已满,产品数为:10
消费者1 获得了一件产品!当前产品数为:9
消费者2 获得了一件产品!当前产品数为:8
当消费者时间间隔小,表示消费的快,会出现仓库为空的情况。测试结果如下。
消费者2 开始等待!当前仓库为空,产品数为:0
生产者3 生产了一件产品!当前产品数为:1
生产者2 生产了一件产品!当前产品数为:2
生产者1 生产了一件产品!当前产品数为:3
消费者2 获得了一件产品!当前产品数为:2
消费者1 获得了一件产品!当前产品数为:1
消费者3 获得了一件产品!当前产品数为:0
消费者3 开始等待!当前仓库为空,产品数为:0
阻塞队列方式
好吧 这人出了点问题 先这样 到时候再来补。