本文部分摘自《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 并发编程 生产者消费者模式的更多相关文章

  1. Java多线程编程——生产者-消费者模式(1)

    生产者-消费者模式在生活中非常常见.就拿我们去餐馆吃饭为例.我们会遇到以下两种情况: 1.厨师-客人 如下图所示,生产者.消费者直接进行交互. 生产者生产出产品后,通知消费者:消费者消费后,通知生产者 ...

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

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

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

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

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

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

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

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

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

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

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

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

  8. Java多线程_生产者消费者模式2

    在我的上一条博客中,已经介绍到了多线程的经典案列——生产者消费者模式,但是在上篇中用的是传统的麻烦的非阻塞队列实现的.在这篇博客中我将介绍另一种方式就是:用阻塞队列完成生产者消费者模式,可以使用多种阻 ...

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

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

随机推荐

  1. Koa & node.js

    KOA https://github.com/koajs/koa https://koajs.com/ $ nvm install 7 # node.js 7 + $ nvm install 10 $ ...

  2. How to using PyPI publish a Python package

    How to using PyPI publish a Python package PyPI & Python package https://pypi.org/ main make a f ...

  3. CSS border gradient color All In One

    CSS border gradient color All In One CSS Gradient Borders border-image-source & border-image-sli ...

  4. CSS rulesets

    CSS rulesets https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax#CSS_rulesets https://css-tricks ...

  5. V8 & ECMAScript & ES-Next

    V8 & ECMAScript & ES-Next ES6, ES7, ES8, ES9, ES10, ES11, ES2015, ES2016, ES2017, ES2018, ES ...

  6. 三万长文50+趣图带你领悟web编程的内功心法:一文带你深入解读HTTP的发展史

    看到题目,大家是不是认为根据上一篇(两万字长文50+张趣图带你领悟网络编程的内功心法)一样,其实不然,我们上一边介绍的是网络编程的基本功,有了这些基本功之后,我们就可以在此之上构建更加接近实际应用的w ...

  7. 不使用的大对象为什么要手动设置null,真的有效吗?

    本文转载自不使用的大对象为什么要手动设置null,真的有效吗? 导语 在我们开发过程中,对于大的对象使用过后,为了help gc ,我们会手动将大对象置为null,背后的原理是什么,是不是最佳的实践. ...

  8. CPU使用率原理及计算方式

    本文转载自CPU使用率原理及计算方式 CPU:超线程和多核 超线程(Hyper-Threading ) 超线程是Intel最早提出一项技术,最早出现在2002年的Pentium4上.单个采用超线程的C ...

  9. apply方法的实现原理

    apply 的核心原理: 将函数设为对象的属性 执行和删除这个函数 指定 this 到函数并传入给定参数执行函数 如果不传参数,默认指向 window Function.prototype.myApp ...

  10. 带你认识webpack

    一.webpack是什么 webpack是一种前端资源构建工具,一个静态模块打包器(module bundler).在webpack看来,前端的所有资源文件(js/json/css/img/less/ ...