Java 并发编程 生产者消费者模式
本文部分摘自《Java 并发编程的艺术》
模式概述
在线程的世界里,生产者就是生产数据的线程,消费者就是消费数据的数据。生产者和消费者彼此之间不直接通信,而是通过阻塞队列进行通信,所以生产者生产完数据后不用等待消费者处理,而是直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列取,阻塞队列相当于一个缓冲区,平衡了生产者和消费者的处理能力
模式实战
假设现有需求:把各部门的邮件收集起来,统一处理归纳。可以使用生产者 - 消费者模式,启动一个线程把所有邮件抽取到队列中,消费者启动多个线程处理邮件。Java 代码如下:
public class QuickCheckEmailExtractor {
private final ThreadPoolExecutor threadsPool;
private final BlockingQueue<EmailDTO> emailQueue;
private final EmailService emailService;
public QuickCheckEmailExtractor() {
emailQueue = new LinkedBlockingQueue<>();
int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
threadsPool = new ThreadPoolExecutor(corePoolSize, corePoolSize, 101,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(2000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
emailService = new EmailService();
}
public void extract() {
// 抽取所有邮件到队列里
new ExtractEmailTask().start();
// 处理队列里的邮件
check();
}
private void check() {
try {
while (true) {
// 两秒内取不到就退出
EmailDTO email = emailQueue.poll(2, TimeUnit.SECONDS);
if (email == null) {
break;
}
threadsPool.submit(new CheckEmailTask());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void extractEmail() {
List<EmailDTO> allEmails = emailService.queryAllEmails();
if (allEmails == null) {
return;
}
for (EmailDTO emailDTO : allEmails) {
emailQueue.offer(emailDTO);
}
}
protected void checkEmail(EmailDTO email) {
System.out.println("邮件" + email.getId() + "已处理");
}
public class ExtractEmailTask extends Thread {
@Override
public void run() {
extractEmail();
}
}
public class CheckEmailTask extends Thread {
private EmailDTO email;
@Override
public void run() {
checkEmail(email);
}
public CheckEmailTask() {
super();
}
public CheckEmailTask(EmailDTO email) {
super();
this.email = email;
}
}
}
多生产者和多消费者场景
在多核时代,多线程并发处理速度比单线程处理速度更快,所以可以使用多个线程来生产数据,多个线程来消费数据。更复杂的情况是,消费者消费完的数据,可能还要交给其他消费者继续处理,如图所示:

我们在一个长连接服务器中使用这种模式,生产者 1 负责将所有客户端发送的消息存放在阻塞队列 1 里,消费者 1 从队列里读消息,然后通过消息 ID 进行散列得到 N 个队列中的一个,然后根据编号将消息存放在不同的队列里,每个阻塞队列会分配一个线程来阻塞队列里的数据。如果消费者 2 无法消费消息,就将消息再抛回阻塞队列 1 中,交给其他消费者处理
public class MsgQueueManager {
/**
* 消息总队列
*/
private final BlockingQueue<Message> messageQueue;
/**
* 消息子队列集合
*/
private final List<BlockingQueue<Message>> subMsgQueues;
private MsgQueueManager() {
messageQueue = new LinkedBlockingQueue<>();
subMsgQueues = new ArrayList<>();
}
public static MsgQueueManager getInstance() {
return new MsgQueueManager();
}
public void put(Message msg) {
try {
messageQueue.put(msg);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public Message take() {
try {
return messageQueue.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return null;
}
/**
* 消费者线程获取子队列
*/
public BlockingQueue<Message> addSubMsgQueue() {
BlockingQueue<Message> subMsgQueue = new LinkedBlockingQueue<>();
subMsgQueues.add(subMsgQueue);
return subMsgQueue;
}
/**
* 消息分发线程,负责把消息从大队列塞到小队列里
*/
class DispatchMessageTask implements Runnable {
/**
* 控制消息分发开始与结束
*/
private boolean flag = true;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
BlockingQueue<Message> subQueue;
while (flag) {
// 如果没有数据,则阻塞在这里
Message msg = take();
// 如果为空,表示没有Session连接,需要等待Session连接上来
while ((subQueue = getSubQueue()) == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 把消息放到小队列里
try {
subQueue.put(msg);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
/**
* 均衡获取一个子队列
*/
public BlockingQueue<Message> getSubQueue() {
List<BlockingQueue<Message>> subMsgQueues = getInstance().subMsgQueues;
if (subMsgQueues.isEmpty()) {
return null;
}
int index = (int) (System.nanoTime() % subMsgQueues.size());
return subMsgQueues.get(index);
}
}
}
Java 并发编程 生产者消费者模式的更多相关文章
- Java多线程编程——生产者-消费者模式(1)
生产者-消费者模式在生活中非常常见.就拿我们去餐馆吃饭为例.我们会遇到以下两种情况: 1.厨师-客人 如下图所示,生产者.消费者直接进行交互. 生产者生产出产品后,通知消费者:消费者消费后,通知生产者 ...
- Java设计模式之生产者消费者模式
Java设计模式之生产者消费者模式 博客分类: 设计模式 设计模式Java多线程编程thread 转载 对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的.就像学习每一门编程语言一 ...
- java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-【费元星Q9715234】
java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-[费元星Q9715234] 说明如下,不懂的问题直接我[费元星Q9715234] 1.反射的意义在于不将xml tag ...
- java设计模式之生产者/消费者模式
什么是生产者/消费者模式? 某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产生数据的模块,就形象地称为生产者:而处理数据的模块,就称为消费者 ...
- 【多线程】java多线程实现生产者消费者模式
思考问题: 1.为什么用wait()+notify()实现生产者消费者模式? wait()方法可以暂停线程,并释放对象锁 notify()方法可以唤醒需要该对象锁的其他线程,并在执行完后续步骤,到了s ...
- Java实现多线程生产者消费者模式的两种方法
生产者消费者模式:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据.生产者生产一个,消费者消费一个,不断循环. 第一种实现方法,用BlockingQueue阻塞队 ...
- Java多线程-----实现生产者消费者模式的几种方式
1 生产者消费者模式概述 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理 ...
- Java多线程_生产者消费者模式2
在我的上一条博客中,已经介绍到了多线程的经典案列——生产者消费者模式,但是在上篇中用的是传统的麻烦的非阻塞队列实现的.在这篇博客中我将介绍另一种方式就是:用阻塞队列完成生产者消费者模式,可以使用多种阻 ...
- java实现多线程生产者消费者模式
1.概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消 ...
随机推荐
- npm published cli package's default install missing the `-g` flag
npm published cli package's default install missing the -g flag https://npm.community/t/npm-publishe ...
- DevOps tools
DevOps tools mozart & ansible https://www.ansible.com/integrations/devops-tools websocket jenken ...
- html5 useful skills blogs
html5 useful skills blogs preload & prefetch https://www.30secondsofcode.org/snippet/ary blogs h ...
- uniapp 发起网络请求
推荐下我写的uni-http 创建http-config.js import Vue from 'vue' const BASE_URL = 'http://xxx.com'; if (process ...
- Dart: List排序
var list = <Item>[ Item(title: "item 1", isTopping: true), Item(title: "item 2& ...
- 科普NGK公链生态板块旗下的BGV、SPC、NGK、USDN四大币种
众所周知,NGK公链所有数据上链.公开透明,NGK公链生态板块目前主要分为四个板块---BGV.SPC.NGK.USDN四大币种,笔者以时间上倒叙手法来一一科普. 首先,是2021新年刚推出的SPC侧 ...
- C++算法代码——笨小猴
题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1163 题目描述 笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼.但是他找到了 ...
- yum安装MySQL8 - Centos8
官方地址:https://dev.mysql.com/doc/refman/8.0/en/linux-installation-yum-repo.html 参考博客地址:https://www.jia ...
- 一文读懂什么是kubernetes?
kubernetes概述 kubernetes面世不过短短几年时间,kuberenetes已经成为容器编排领域事实上的标准,无论是公有云,私有云或混合云,kubernetes都将作为一个为任何应用,任 ...
- Docker的架构
一.Docker引擎 docker引擎是一个c/s结构的应用,主要组件见下图: Server是一个常驻进程 REST API 实现了client和server间的交互协议 CLI 实现容器和镜像的管理 ...