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阻塞队列方法、信号量、管道等方法实现。

java实现多线程生产者消费者模式的更多相关文章

  1. java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-【费元星Q9715234】

    java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-[费元星Q9715234] 说明如下,不懂的问题直接我[费元星Q9715234] 1.反射的意义在于不将xml tag ...

  2. Java实现多线程生产者消费者模式的两种方法

    生产者消费者模式:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据.生产者生产一个,消费者消费一个,不断循环. 第一种实现方法,用BlockingQueue阻塞队 ...

  3. Java设计模式之生产者消费者模式

    Java设计模式之生产者消费者模式 博客分类: 设计模式 设计模式Java多线程编程thread 转载 对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的.就像学习每一门编程语言一 ...

  4. java多线程 生产者消费者模式

    package de.bvb; /** * 生产者消费者模式 * 通过 wait() 和 notify() 通信方法实现 * */ public class Test1 { public static ...

  5. Java 并发编程 生产者消费者模式

    本文部分摘自<Java 并发编程的艺术> 模式概述 在线程的世界里,生产者就是生产数据的线程,消费者就是消费数据的数据.生产者和消费者彼此之间不直接通信,而是通过阻塞队列进行通信,所以生产 ...

  6. java设计模式之生产者/消费者模式

    什么是生产者/消费者模式? 某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产生数据的模块,就形象地称为生产者:而处理数据的模块,就称为消费者 ...

  7. Java实现多线程生产者消费者模型及优化方案

    生产者-消费者模型是进程间通信的重要内容之一.其原理十分简单,但自己用语言实现往往会出现很多的问题,下面我们用一系列代码来展现在编码中容易出现的问题以及最优解决方案. /* 单生产者.单消费者生产烤鸭 ...

  8. Java多线程-----实现生产者消费者模式的几种方式

       1 生产者消费者模式概述 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理 ...

  9. 【多线程】java多线程实现生产者消费者模式

    思考问题: 1.为什么用wait()+notify()实现生产者消费者模式? wait()方法可以暂停线程,并释放对象锁 notify()方法可以唤醒需要该对象锁的其他线程,并在执行完后续步骤,到了s ...

随机推荐

  1. 【算法】main函数的堆栈溢出

    main函数的堆栈的大小默认为1mb 如果把数组int x[1000][1000]定义在main函数里 则int为4byte,8bit为1byte,1024byte为1kb,1024kb为1mb 4* ...

  2. Interval Bound Propagation (IBP)

    目录 概 主要内容 IBP CROWN CROWN-IBP 训练的技巧 写在最后 代码 Gowal S., Dvijotham K., Stanforth R., Bunel R., Qin C., ...

  3. Java Web程序设计笔记 • 【第8章 会话跟踪技术进阶】

    全部章节   >>>> 本章目录 8.1 Session机制 8.1.1 Session 简介 8.1.2 创建 HttpSession 实例 8.1.3 HttpSesiso ...

  4. Java基础寒假作业-个人所得税计算系统

    <个人所得税计算系统>设计 一.需求说明 设计一个简易的个人所得税计算系统,通过输入个人应发工资计算出各个地区的三险(医疗保险.养老保险)一金(公积金)和个人所得税.系统需要实现用户登录. ...

  5. Java初学者作业——编写Java程序, 实现根据用户购买商品总金额, 计算实际支付的金额及所获得的购物券金额。

    返回本章节 返回作业目录 需求说明: 编写Java程序, 实现根据用户购买商品总金额, 计算实际支付的金额及所获得的购物券金额. 购买总金额达到或超过 1000元,按 8折优惠,送 200元的购物券: ...

  6. 编写Java程序,使用 dom4j 解析上一节王者荣耀“英雄”对应的Xml文件数据内容,打印输出,具体格式

    查看本章节 查看作业目录 需求说明: 使用 dom4j 解析上一节王者荣耀"英雄"对应的Xml文件数据内容,打印输出,具体格式如图所示 实现思路: 创建ParseHeroXML用于 ...

  7. Kylin启动步骤

    Kylin本身的启动命令比较简单, 但是由于Kylin依赖的其他组件比较多, 所以把完整的启动步骤整理一下. 1.确保集群时间同步 首先查看集群中的时间是否同步 date 如果时间不一致,需要使用如下 ...

  8. 初识python之词组截取及翻译

    d = {} k = [] v = [] with open('dir','r',encoding='utf-8') as f: for i in f.readlines(): j = i.strip ...

  9. shell 之 循环执行某操作

    需求: 按月执行201904到202010的py脚本. 代码如下: #!/bin/bash i=201904 # 定义开始月份 while [ $i -le 202010 ] # 当i小于等于2020 ...

  10. Linux_yum安装时报404错误

    使用yum安装报错如下: [root@localhost ~]# yum install gcc 已加载插件:fastestmirror Loading mirror speeds from cach ...