1、概念

  生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。

  在实际生活中,老师和学生的关系就是一种生产者消费者模型,老师负责布置作业(生产),学生负责写作业(消费)。还有餐馆厨师负责做饭(生产),顾客负责吃饭(消费)等等。

2、参与对象

  生产者:负责向缓冲区存放数据

  消费者:负责从缓冲区读取数据

  缓冲区:负责存放数据

3、优点

  3.1、解耦

    假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。

  3.2、支持并发

    生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者就会白白糟蹋大好时光。

使用了生产者/消费者模式之后,生产者和消费者可以是两个独立的并发主体(常见并发类型有进程和线程两种,后面的帖子会讲两种并发类型下的应用)。生产者把制造出来的数据往缓冲区一丢,就可以再去生产下一个数据。基本上不用依赖消费者的处理速度。

其实当初这个模式,主要就是用来处理并发问题的。

  3.3、支持忙闲不均

    缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。

4、代码实现

  说明:例子模拟生产者(Producer)消费者(Consumer)模式,其中缓冲区(Bean),当缓冲区数据达到10(FULL),生产者停止生产数据,当缓冲区没有数据,消费者停止消费数据。

  4.1、Bean

public class Bean {
private Integer count; public Integer getCount() {
return count;
} public void setCount(Integer count) {
this.count = count;
}
}

  4.2、生产者

public class Producer implements Runnable {private Bean bean;

    private static final Integer FULL = 10;

    public Producer(Bean bean) {
this.bean = bean;
} private void produce() throws InterruptedException {
while (true){
Thread.sleep(1000);
synchronized (bean) {
if (bean.getCount() >= FULL) {
System.out.println("生产者等待");
bean.wait();
}
else {
bean.setCount(bean.getCount() + 1);
System.out.println(Thread.currentThread().getName() + "生产, current :" + bean.getCount());
bean.notifyAll();
}
} }
} @Override
public void run() {
try {
produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

  4.3、消费者

public class Consumer implements Runnable {
private Bean bean; private static final Integer NULL = 0; public Consumer(Bean bean) {
this.bean = bean;
} private void consume() throws InterruptedException {
while (true) {
Thread.sleep(2000);
synchronized (bean) {
if (bean.getCount() <= NULL) {
System.out.println("消费者等待");
bean.wait();
}
else {
bean.setCount(bean.getCount() - 1);
System.out.println(Thread.currentThread().getName() + "消费, current :" + bean.getCount());
bean.notifyAll();
}
}
}
} @Override
public void run() {
try {
consume();
} catch (Exception e) {
e.printStackTrace();
}
}
}

  4.4、main

public class Test {
public static void main(String[] args) {
Bean bean = new Bean();
bean.setCount(0);
for (int i = 0; i < 5; i++) {
new Thread(new Producer(bean)).start();
new Thread(new Consumer(bean)).start();
}
}
}

  4.5、输出

Thread-0生产, current :1
Thread-8生产, current :2
Thread-2生产, current :3
Thread-6生产, current :4
Thread-4生产, current :5
Thread-2生产, current :6
Thread-7消费, current :5
Thread-9消费, current :4
Thread-0生产, current :5
Thread-3消费, current :4
Thread-1消费, current :3
Thread-8生产, current :4
Thread-6生产, current :5
Thread-5消费, current :4
Thread-4生产, current :5
Thread-8生产, current :6
Thread-2生产, current :7
Thread-0生产, current :8
Thread-4生产, current :9
Thread-6生产, current :10
生产者等待
生产者等待

为什么要在count外面套一层Bean?不直接使用count呢

原因是缓冲区(count)需要对生产者消费者共享,synchronize不能锁Integer对象(操作Integer对象的时候,自动装箱/拆箱对象就已经变了,会失去同步的效果)

5、总结

以上是通过wait()/notifyAll()实现的生产者消费者模式,也是比较简单的一种实现方式,除此之外还有通过await() / signal()方法、BlockingQueue阻塞队列方法、信号量、管道等方法实现。

最新文章

  1. webstorm--破解
  2. 基于 flow.ci 实现 PHP 项目自动化持续集成
  3. HTML5自学笔记[ 15 ]canvas绘图基础6
  4. 磁盘阵列RAID
  5. (0)写给Web初学者的教案-----Web基础
  6. 内嵌的Component调用外部的方法
  7. nodejs本地服务器自动重启
  8. 修复mysql表
  9. android之monkey测试
  10. aways on 配置部署(一)——准备工作
  11. Activity的启动流程
  12. python windows环境响铃
  13. Got fatal error 1236 from master when reading data from binary log: &#39;Client requested master to start replication from impossible position
  14. 【JMeter】获取JDBC响应做接口关联
  15. C# 异常和异常处理
  16. csv文件的读写
  17. ijcai statistics
  18. Sublime2编译Python程序EOFError:EOF when reading a line解决方法【转】
  19. 摘:ClickOnce部署
  20. Adobe Acrobat Pro 修改背景色

热门文章

  1. BBN+
  2. Notepad++插件Base64编解码
  3. Kafka基础教程(一):认识Kafka
  4. .net core中EFCore发出警告:More than twenty &#39;IServiceProvider&#39; instances have been created for internal use by Entity Framework
  5. strict weak ordering导致公司级故障
  6. MySQL存储过程入门基础
  7. APP自动化测试之手机滑屏
  8. linux 之 DolphinScheduler 安装步骤
  9. idea 开启 tomcat 热部署 的 具体流程 和 使用方式
  10. react中prop-types的使用