生产者与消费者的实现

一到多个线程充当生产者,生产元素。一到多个线程充当消费者,消费元素。在两者之间插入一个队列充当缓冲区,建立起生产者和消费者的松散耦合。

正常情况下,即生产元素的速度和消费元素的速度差不多时,生产者和消费者其实是不需要去关注对方的。

生产者可以一直生产,因为队列里总是有空间。消费者可以一直消费,因为队列里总是有元素。即达到一个动态的平衡。

但在特殊情况下,比如生产元素的速度很快,队列里没有了空间,此时生产者必须自我“停工”,开始“休息”。

一旦消费者消费了元素之后,队列里才会有空间,生产者才可以重启生产,所以,消费者在消费完元素后有义务去叫醒生产者复工。

更准确的说法应该是,只有在生产者“休息”时,消费者消费完元素后才需要去叫醒生产者。否则,其实可以不用叫醒,因为人家本来就没休息。

反之,如果消费元素的速度很快,队列里没有了元素,只需把上述情况颠倒过来即可。

但这样的话就会引入一个新的问题,就是要能够准备的判断出对方有没有在休息,为此就必须定义一个状态变量,在自己即将开始休息时,自己设置下这个变量。

对方通过检测这个变量,来决定是否进行叫醒操作。当自己被叫醒后,首先要做的就是清除一下这个变量,表明我已经醒来复工了。

这样就需要多维护一个变量和多了一部分判断逻辑。可能有些人会觉得可以通过判断队列的“空”或“满”(即队列中的元素数目)来决定是否进行叫醒操作。

在高并发下,可能刚刚判断队列不为空,瞬间之后队列可能已经变为空的了,这样会导致逻辑出错。线程可能永远无法被叫醒。

因此,综合所有,生产者每生产一个元素后,都会通知消费者,“现在有元素的,你可以消费”。

同样,消费者每消费一个元素后,也会通知生产者,“现在有空间的,你可以生产”。

很明显,这些通知很多时候(即对方没有休息时)是没有真正意义的,不过无所谓,只要忽略它们就行了。

首先要保证是正确的,“宁可错杀一千,也不放过一个”。

常见的生产者与消费者的实现有以下几种:

  • Synchronized的wait 和notifyAll;
  • ReentrantLock的await 和signalAll;
  • 阻塞队列ArrayBlockingQueue等。

下面以lock实现简单生产者和消费者:

 public static void main(String[] args) {Queue queue = new Queue();new Thread(new Producer(queue)).start();new Thread(new Producer(queue)).start();new Thread(new Consumer(queue)).start();}/**** 生产者**/static class Producer implements Runnable {Queue queue;Producer(Queue queue) {this.queue = queue;}@Overridepublic void run() {try {for (int i = 0; i < 10000; i++) {doingLongTime();queue.putEle(random(10000));}} catch (Exception e) {e.printStackTrace();}}}/**** 消费者**/static class Consumer implements Runnable {Queue queue;Consumer(Queue queue) {this.queue = queue;}@Overridepublic void run() {try {for (int i = 0; i < 10000; i++) {doingLongTime();queue.takeEle();}} catch (Exception e) {e.printStackTrace();}}}/**** 生产队列**/static class Queue {Lock lock = new ReentrantLock();Condition prodCond  = lock.newCondition();Condition consCond = lock.newCondition();final int capacity= 20;Object[] container = new Object[capacity];int count = 0;int putIndex = 0;int takeIndex = 0;public void putEle(Object ele) throws InterruptedException {try {lock.lock();while (count == capacity) {println("队列已满:%d,生产者开始休息。。。", count);prodCond.await();}container[putIndex] = ele;println("生产元素:%d", ele);putIndex++;if (putIndex >= capacity) {putIndex = 0;}count++;println("通知消费者去消费。。。");consCond.signalAll();} finally {lock.unlock();}}public Object takeEle() throws InterruptedException {try {lock.lock();while (count == 0) {println("队列已空:%d,消费者开始休息。。。", count);consCond.await();}Object ele = container[takeIndex];println("消费元素:%d", ele);takeIndex++;if (takeIndex >= capacity) {takeIndex = 0;}count--;println("通知生产者去生产。。。");prodCond.signalAll();return ele;} finally {lock.unlock();}}}

生产者与消费者的实现相关推荐

  1. RabbitMQ 入门系列(2)— 生产者、消费者、信道、代理、队列、交换器、路由键、绑定、交换器

    本系列是「RabbitMQ实战:高效部署分布式消息队列」和 「RabbitMQ实战指南」书籍的读书笔记. RabbitMQ 中重要概念 1. 生产者 生产者(producer)创建消息,然后发送到代理 ...

  2. linux进程间通信:system V 信号量 生产者和消费者模型编程案例

    生产者和消费者模型: 有若干个缓冲区,生产者不断向里填数据,消费者不断从中取数据 两者不冲突的前提: 缓冲区有若干个,且是固定大小,生产者和消费者各有若干个 生产者向缓冲区中填数据前需要判断缓冲区是否 ...

  3. Python中的生产者与消费者模式(转载)

    利用多线程和队列可以实现生产者消费者模式.该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度. 1.什么是生产者和消费者? 在线程世界里,生产者就是生产数据(或者说发布任务)的线程 ...

  4. 12.多线程的实现方式、线程安全问题的产生与解决以及生产者与消费者问题

    一.实现多线程 1.1 了解多线程 多线程是指从软件或者硬件上实现多个线程并发执行的技术,具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,从而提升性能. 1.2 并发与并行 并行是指同 ...

  5. java多线程 生产者消费者_java多线程之-生产者与消费者

    java多线程之-并发协作[生产者与消费者]模型 对于多线程程序来说,不管c/c++ java python 等任何编程语言,生产者与消费者模型都是最为经典的.也就是可以说多线程的并发协作 对于此模型 ...

  6. python生产和消费模型_python queue和生产者和消费者模型

    queue队列 当必须安全地在多个线程之间交换信息时,队列在线程编程中特别有用. classqueue.Queue(maxsize=0) #先入先出classqueue.LifoQueue(maxsi ...

  7. 基于java多线程来实现生产者和消费者的实例

    声明:本实例是在网上看到,做了很小的修改.所以感谢之前的作者.只是一时忘了哪儿看到,没法加入链接,向原作者道歉,以示尊重.抱歉-^)... 同步栈: 1 class SycnStack { 2 pri ...

  8. PV操作——生产者和消费者

    首先,先来看几个概念: 同步:协作的过程,比如,多人开发合作. 相互排斥:争抢资源的过程.比如苦逼的大学选课: 临界区:进程中对临界资源实施操作的那段程序: 临界资源:一次仅仅能一个进程使用的资源,比 ...

  9. 线程通信问题--生产者和消费者问题

    一.问题引入:首先实现一个线程通信的实例,使用两个线程交替打印输出100以内的数字. 代码实现如下: 1 package com.baozi.exer; 2 3 public class Commun ...

  10. Java中生产者与消费者问题的演变

    想要了解更多关于Java生产者消费者问题的演变吗?那就看看这篇文章吧,我们分别用旧方法和新方法来处理这个问题. 生产者消费者问题是一个典型的多进程同步问题. 对于大多数人来说,这个问题可能是我们在学校 ...

最新文章

  1. [转]软件项目版本号的命名规则及格式
  2. 联想超融合,打通智慧医疗的“症结”
  3. 【Spring注解系列11】Spring后置处理器BeanPostProcessor用法与原理
  4. 【高效JDBC编程工具JadePool快速入门】
  5. springboot配置文件的加载顺序(./config目录优先级最高)
  6. [dp][前缀和] Jzoj P5907 轻功(qinggong)
  7. P3168-[CQOI2015]任务查询系统【主席树】
  8. 计算机音乐奔跑,跑起来!100首最适合跑步的音乐
  9. redis 值字符串前面部分乱码_redis key乱码
  10. 性能优化篇(2):不能忽视的DOM元素
  11. 生活中的这些难题,数据库开发者可为你解决!
  12. TensorFlow 学习指南 三、学习
  13. KmeansDBSCAN
  14. Kotlin 1.0 正式版发布啦
  15. GetTickCount函数
  16. Latex/CTex/WinEdt 期刊双栏排版图表中英文标题走过的那些坑
  17. 1413. 逐步求和得到正数的最小值
  18. 艾克姆 蓝牙nRF52832上册-学习笔记
  19. 【已解决】体视显微镜左右成像大小不一致
  20. 网络视频无法快进无法选中进度条

热门文章

  1. Linux tail命令的使用方法详解
  2. Dual Super-Resolution Learning for Semantic Segmentation解读
  3. HFDS 内部工作机制
  4. Qt 图片 等比例显示、放大、缩小、平移图像 显示
  5. 计算机等级考试 12月,2020年12月计算机等级考试成绩什么时候公布
  6. Ubuntu 使用命令向QQ邮箱发送邮件
  7. python3.7通过itchat方式登录微信给好友发送天气信息
  8. 自学C语言入门教程(上)
  9. Git error: cannot spawn ssh: No such file or directory的一个解决办法
  10. 爬虫实战5:豆瓣读书爬取