一、什么是生产者消费者模式

生产者生产数据存放在缓冲区,消费者从缓冲区拿出数据处理。

可能大家会问这样有何好处?

1.解耦

由于有了缓冲区,生产者和消费者之间不直接依赖,耦合度降低,便于程序拓展和维护。

如果没有缓冲区消费者与生产者是直连的,改动生产者可能对消费者造成影响。

2.并发处理,提升效率

消费者和生产者分离后,两者不形成依赖可以独立运行,提高了效率。

如果是消费者和生产者是直接接触没有缓冲区,假如消费者消费太慢,生产者也只能等待消费者消费完。这样浪费资源。

中间多个缓冲区后,消费者消费慢生产者可以先把生产的东西放在缓冲区,然后等待消费者慢慢消费。

举个例子,就像寄信我们是寄信人可以看做生产者(生产了信)。邮递员看做消费者(处理我们的信),缓冲区就看做是邮筒。

没有缓冲区的话我们就要直接和快递员打交道,假如我们做出什么更改邮递员也需要同样做出更改满足我们的需求。

而且没有邮筒的话,而且每次邮递员都需要找我们取信,效率低下。

假如邮递员处理较慢我们只能等着极其不方便,如果有了邮筒邮递员比较忙我们可以先放在邮筒里等邮递员慢慢处理,这样不妨碍我们寄信。

接下来我们梳理下生产者消费者模式的基本思路。

要实现消费者生产者模式我们还需要知道几个函数

1.wait(),让当前线程等待且会释放对象锁,这时wait()之后的语句不会执行。只有被其他线程用notify或notifyall唤醒,才能重新抢夺锁,获得锁后从wait()方法之后的路径继续执行

如果没有被其他线程唤醒,就不会醒来。

2.nitify()通知在此对象监视器上等待的单个线程醒来,只是通知而且通知是随机的。当前对象监视器上等待的线程获得通知后会金进入等待队列抢夺对象锁。

3.notifyall唤醒在此对象监视器上等待的所有线程。

注意上面的函数都需要获得对象锁,所以需要synchronized配合使用。

接下来就是具体的代码了:

 缓冲区:

public class Buffer {
private int index = 0;
private int MAX = 5; //定义缓冲区的最大存储量
private Phone pa[] = new Phone[MAX];//定义存储产品的数组 synchronized public void consumer(){ //消费者方法,要加synchronized获得锁,消费线程会进缓冲区拿东西。
if(index <= 0){ //如果缓冲区没有资源,当前线程等待并释放锁。
try {
wait(); //如果该线程被其他线程唤醒并获得锁,从wait()之后继续执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} /**
* 消费时一定要注意先--,因为必定先生产后才能消费,
* 而生产后由于++的原因会多加一个1,所以这里需要多减一个1.
* 仓库有货或生产者线程唤醒后消费产品。
* 生产者是先生产然后唤醒消费者,所以唤醒消费者后的缓冲区必有货物.
*/ //从缓冲区拿手机,即消费手机。
System.out.println("消费手机:" + pa[--index].getPhoneNum());
notify(); //消费后唤醒生产者线程。

/**
* 这里的唤醒只是通知下可以抢夺锁了,但也不一定抢到。
* 如果生产者没有抢到锁消费者会继续消费。
* 如果没有货了,消费者自己会等待,然后生产者自然会去生产。
* 锁只有执行完synchronized范围才会释放锁。wait()是可以直接立刻马上释放锁,
* 而且之后的语句也不执行,之后的语句等到下一次被其他线程唤醒并获得锁后才执行。
*/
} synchronized public void producer(Phone p){//生产者,接受来自生产线程生产的产品。
if(index >= MAX){ //如果缓冲区已满,则释放锁并等待。
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//将生产的产品放在数组中,放入后要加1
pa[index++] = p; //仓库没有满或被消费者线程唤醒后才能生产。
//被消费者叫醒说明 消费者消费了产品,则缓冲区必定没有满.
System.out.println("生产手机:" + p.getPhoneNum());
notify(); //生产后,唤醒消费者线程。
}
}

产品:

public class Phone {
private int num; public Phone(int num){ //构造器设置手机编号
this.num = num;
} public int getPhoneNum(){ //产品中有一个取得当前手机编号的方法。
return num;
}
}

 

生产者

public class Producer implements Runnable{  //生产者线程需要实现Runnable接口
private int i = 0;
private Buffer b; //创建缓冲区对象。 public void setBuffer(Buffer b){ //添加构造方法获得缓冲区对象。
this.b = b;
} public void run(){ //生产者线程生产20个产品
for(i = 0; i < 20; i++){
b.producer(new Phone(i)); //生产者线程将产品放入缓冲区
}
}
}

消费者

public class Consumer implements Runnable{
private Buffer b; public void setBuffer(Buffer b){ //通过方法设置缓冲区对象
this.b = b;
} public void run(){ //消费者线程
for(int i = 0; i < 20; i++){ //消费20个产品
b.consumer(); //进入缓冲区拿产品
}
}
}

客户端

public class ProCon {
public static void main(String[] args) {
Buffer b = new Buffer(); //建立缓冲区 Consumer c = new Consumer(); //建立消费者线程
c.setBuffer(b); //在消费者线程中放入缓冲区对象
Producer p = new Producer(); //建立生产者线程
p.setBuffer(b); //在生产者线程中放入缓冲区对象 new Thread(c).start();
new Thread(p).start();
}
}
运行结果:
生产手机:0
生产手机:1
生产手机:2
生产手机:3
生产手机:4
消费手机:4
消费手机:3
消费手机:2
消费手机:1
消费手机:0
生产手机:5
生产手机:6
生产手机:7
生产手机:8
生产手机:9
消费手机:9
消费手机:8
消费手机:7
消费手机:6
消费手机:5
生产手机:10
生产手机:11
生产手机:12
生产手机:13
生产手机:14
消费手机:14
消费手机:13
消费手机:12
消费手机:11
消费手机:10
生产手机:15
生产手机:16
生产手机:17
生产手机:18
生产手机:19
消费手机:19
消费手机:18
消费手机:17
消费手机:16
消费手机:15

由于这里缓冲区的容量为5,所以每生产5个缓冲区就满了,然后会通知生产者会等待,消费者回来消费。

当消费者消费了5个后没有货物了,消费者会等待,这时生产者又会来生产。

这只是恰好一个线程运行的时间可以生产完5个或者消费5个,在实际中的顺序是不确定的。

但有一点可以确定,必定是生产好了后的产品才能消费。

而且必定是缓冲区满了生产者会停下,缓冲区没有货物了消费者会停下。

最新文章

  1. 用github来展示你的前端页面吧
  2. Anaconda 用于科学计算的 Python 发行版
  3. c、c++ 常用函数记录
  4. CentOS6.7安装RabbitMQ3.6.5
  5. Tomcat单向Https验证搭建,亲自实现与主流浏览器、Android/iOS移动客户端安全通信
  6. unity3d 镜头随鼠标移动
  7. 项目解析- JspLibrary - part1
  8. [转]centos7 配置yum源(本地+光盘)
  9. python2.7爬取豆瓣电影top250并写入到TXT,Excel,MySQL数据库
  10. codeforces #313 div1 A
  11. 网站报错Access denied for user &#39;root&#39;@&#39;localhost&#39; -问题排查续
  12. autoitv3点击windows界面
  13. 关于.net 对.manifest清单文件查找缓存的猜想
  14. hdu 4602 Partition(矩阵快速幂乘法)
  15. CSS属性定义 文本修饰 边框效果 背景修饰
  16. codeforces 985C Liebig&#39;s Barrels
  17. Tomcat8.0.11优化相关
  18. jQuery 操作input select,checkbox
  19. 客户端禁用cookie
  20. pytorch 绘制训练曲线;服务器端训练,本地浏览器显示,本地打不开;tensorboard端口被占

热门文章

  1. ASP.NET Core 认证与授权[1]:初识认证 (笔记)
  2. (转\整)UE4游戏优化 多人大地型游戏的优化(三)GPU的优化
  3. win10 ubuntu16双系统安装教程
  4. Python 第一周编程作业
  5. Qt 中C++ static_cast 和 reinterpret_cast的区别
  6. 那些牛掰的 HTML5的API(二)
  7. nodejs &amp; docker
  8. P2215 [HAOI2007]上升序列
  9. 牛客 NOIp模拟1 T1 中位数 解题报告
  10. select下拉框右对齐,去掉箭头,替换箭头