java实现多线程生产者消费者模式
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实现多线程生产者消费者模式的更多相关文章
- java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-【费元星Q9715234】
java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-[费元星Q9715234] 说明如下,不懂的问题直接我[费元星Q9715234] 1.反射的意义在于不将xml tag ...
- Java实现多线程生产者消费者模式的两种方法
生产者消费者模式:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据.生产者生产一个,消费者消费一个,不断循环. 第一种实现方法,用BlockingQueue阻塞队 ...
- Java设计模式之生产者消费者模式
Java设计模式之生产者消费者模式 博客分类: 设计模式 设计模式Java多线程编程thread 转载 对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的.就像学习每一门编程语言一 ...
- java多线程 生产者消费者模式
package de.bvb; /** * 生产者消费者模式 * 通过 wait() 和 notify() 通信方法实现 * */ public class Test1 { public static ...
- Java 并发编程 生产者消费者模式
本文部分摘自<Java 并发编程的艺术> 模式概述 在线程的世界里,生产者就是生产数据的线程,消费者就是消费数据的数据.生产者和消费者彼此之间不直接通信,而是通过阻塞队列进行通信,所以生产 ...
- java设计模式之生产者/消费者模式
什么是生产者/消费者模式? 某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产生数据的模块,就形象地称为生产者:而处理数据的模块,就称为消费者 ...
- Java实现多线程生产者消费者模型及优化方案
生产者-消费者模型是进程间通信的重要内容之一.其原理十分简单,但自己用语言实现往往会出现很多的问题,下面我们用一系列代码来展现在编码中容易出现的问题以及最优解决方案. /* 单生产者.单消费者生产烤鸭 ...
- Java多线程-----实现生产者消费者模式的几种方式
1 生产者消费者模式概述 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理 ...
- 【多线程】java多线程实现生产者消费者模式
思考问题: 1.为什么用wait()+notify()实现生产者消费者模式? wait()方法可以暂停线程,并释放对象锁 notify()方法可以唤醒需要该对象锁的其他线程,并在执行完后续步骤,到了s ...
随机推荐
- What Makes for Good Views for Contrastive Learning
目录 概 前 InfoMin Sweet Spot 空间距离 Color Spaces Frequency Separation 构建 novel views 无监督 半监督 Tian Y., Sun ...
- 【云开发】10分钟零基础学会做一个快递查询微信小程序,快速掌握微信小程序开发技能(轮播图、API请求)
大家好,我叫小秃僧 这次分享的是10分钟零基础学会做一个快递查询微信小程序,快速掌握开发微信小程序技能. 这篇文章偏基础,特别适合还没有开发过微信小程序的童鞋,一些概念和逻辑我会讲细一点,尽可能用图说 ...
- EDP转LVDS屏转接板方案|基于INTELX86主板和商显应用EDP转LVDS设计CS5211
众所周知LVDS接口是美国NS美国国家半导体公司为克服以TTL电平方式传输宽带高码率数据时功耗大,电磁干扰大等缺点而研制的一种数字视频信号传输方式.由于其采用低压和低电流驱动方式,实现了低噪声和低功耗 ...
- .net core中EFCore发出警告:More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework
最近使用.net core k开发时,碰到个问题,Ef使用中程序发出了一个警告: More than twenty 'IServiceProvider' instances have been cre ...
- mysql语句2-单表查询
mysql 查询以及多表查询 以下所有表格样例都采用下边这个表格 mysql> select * from benet; +------+------+----------+ | id | ...
- Nginx.d 设置
#vi nginx.conf 最后一行添加 #加载conf.d内文件 include /usr/local/nginx/conf/conf.d/*.conf; 示例 cd conf.d Vi ** ...
- 利用quake捡洞
quake一开漏洞全靠捡 定位资产 通过主域名定位子域名资产 domain:"target.com" 通过C段定位资产 ip: "1.1.1.1/24" 通过证 ...
- python2.7发送邮件失败之——邮箱安全问题
使用python2.7发送邮件,通过脚本调试,脚本运行通过成功发出了邮件,但是目标邮箱qq没有收到. 刚开始怀疑脚本问题,上网查找资料后,发现邮箱发送成功后目标邮件没有收到有可能有以下几种原因: 1. ...
- 【刷题-LeetCode】190 Reverse Bits
Reverse Bits Reverse bits of a given 32 bits unsigned integer. Example 1: Input: 0000001010010100000 ...
- 【get√】golang新手理解了一点点context
测试代码如下: package practice import ( "context" "log" //"fmt" "time&q ...