1、生产者消费者模式

生产者消费者问题(Producer-consumer problem),也称为有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享大小缓冲区的线程---既所谓的“生产者”和“消费者”----在实际运行时会发生的问题。生产者的主要作用时生成一定量的数据放到缓冲区中,然后重复此过程。以此同时,消费者也在缓冲区消费这些数据。该问题的关键是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消费数据。
       要解决这些问题,就必须让生产者在缓冲区满时休眠(要不干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。统一,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。如果解决办法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。

生产者和消费者模式:这其实是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
对于生产者,没有生产产品之前,要通知消费者等待。而生产了产品之后,又需要马上通知消费者消费。
对于消费者,消费之后,要通知生产者已经消费完成,需要继续生产新的产品给消费者消费。
在生产者和消费者问题中,仅有synchronizede是不够的
synchronized可阻止并发更新同一个共享资源,实现了同步
synchronized不能用来实现不同线程之间的消息传递(通信)
线程通信:并发协助模型“生产者/消费者模式”->可用信号灯法。

2、信号灯法

信号灯法判断生产者(消费者)是否能能够放入(取出)产品的依据是标志位。

设置一个标识,当标识为真的时候,消费者消费,生产者等待。当标识为假的时候,生产者生产,消费者等待.

Java提供了3个方法(java.lang.Object类的方法)解决线程之间的通信问题

方法名:这些方法只能放在同步里。
final void wait()  作用:表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁。
final void wait(long timeout)  作用:指定等待的毫秒数
final void notify()  作用:唤醒一个处于等待状态的线程
final void notifyAll()  作用:唤醒同一个对象上所有调用wait()方法的线程,优先级别搞得线程优先调度。

3、Object的wait/notify+信号灯法代码示例

package runnable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/*** 一个场景,共同的资源* 生产这消费者模式,信号灯法* wait():等待,释放锁,sleep不释放锁* notify()/notifyAll():唤醒*/
class Tea{private String teaName;  //奶茶//信号灯//flag=T,生产者生产,消费者等待,生产完成后通知消费。//flag-F,消费者消费,生产者等待,消费完成后通知生产。boolean flag=true;  //信号灯public synchronized void Make(String teaName){if(!flag){  //生产者等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//制作奶茶System.out.println(Thread.currentThread().getName()+",奶茶店制作了:"+teaName);//制作完成this.teaName=teaName;//通知消费者this.notifyAll();//信号灯设置为falsethis.flag=false;}public synchronized void Take(){if(flag){  //消费者等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//消费者消费System.out.println(Thread.currentThread().getName()+",消费者拿走了:"+teaName);//通知生产者this.notifyAll();//设置信号灯为truethis.flag=true;}
}//生产者
class Maker implements Runnable{public Tea tea;public Maker(Tea tea){this.tea=tea;}@Overridepublic void run() {for(int i=0;i<10;i++){if (i%2==0){this.tea.Make("珍珠奶茶");}else{this.tea.Make("香草奶茶");}}}
}//消费者
class Taker implements Runnable{public Tea tea;public Taker(Tea tea){this.tea=tea;}@Overridepublic void run() {for(int i=0;i<10;i++){tea.Take();}}
}//多线程执行
public class TeaTrading {public static void main(String[] args){//共同的资源Tea tea=new Tea();//生产者和消费者对象,使用同样的资源Maker marker=new Maker(tea);Taker taker=new Taker(tea);new Thread(marker).start();new Thread(taker).start();}
}

在使用线程的wait()等待/notify()通知机制时,一般都要配合一个 boolean 变量值(或者其他能够判断真假的条件),在 notify 之前改变该 boolean 变量的值,保证了程序的正确性。

执行结果:

参考:一篇文章,让你彻底弄懂生产者--消费者问题 - 简书 (jianshu.com)

生产者消费者模式-Java实现 - 天目山电鳗 - 博客园 (cnblogs.com)

Java 多线程详解(四)------生产者和消费者 - YSOcean - 博客园 (cnblogs.com)

JAVA多线程之生产者消费者模型_L_R-CSDN博客_多线程生产者消费者

生产者、消费者问题(管程法与信号灯法) - 墨染念颖 - 博客园 (cnblogs.com)

Java并发编程系列18:多线程之生产者和消费者模式_信号灯法(wait/notify通知机制)相关推荐

  1. python并发编程之semaphore(信号量)_Python 并发编程系列之多线程

    Python 并发编程系列之多线程 2 创建线程 2.1 函数的方式创建线程 2.2 类的方式创建线程 3 Thread 类的常用属性和方法 3.1 守护线程: Deamon 3.2 join()方法 ...

  2. Java并发编程系列

    Java并发编程系列 2018-03-08 Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) ...

  3. 并发编程系列之五多线程synchronized是可重复加锁,重入锁

    并发编程系列之五多线程synchronized是可重复加锁,重入锁.对于重入锁的概念就是可以重复的加锁.. 示例1,在同一个类里面进行加锁,不同的方法调用,都一层一层的嵌套进行加锁,示例1演示重入锁的 ...

  4. Java 并发编程系列之带你了解多线程

    早期的计算机不包含操作系统,它们从头到尾执行一个程序,这个程序可以访问计算机中的所有资源.在这种情况下,每次都只能运行一个程序,对于昂贵的计算机资源来说是一种严重的浪费. 操作系统出现后,计算机可以运 ...

  5. 『图解Java并发编程系列』10张图告诉你Java并发多线程那些破事

    目录 线程安全问题 活跃性问题 性能问题 有态度的总结 头发很多的程序员:『师父,这个批量处理接口太慢了,有什么办法可以优化?』架构师:『试试使用多线程优化』第二天头发很多的程序员:『师父,我已经使用 ...

  6. java并发实战编程pdf_「原创」Java并发编程系列25 | 交换器Exchanger

    2020年Java面试题库连载中 [000期]Java最全面试题库思维导图 [001期]JavaSE面试题(一):面向对象 [002期]JavaSE面试题(二):基本数据类型与访问修饰符 [003期] ...

  7. Java 并发编程系列之闭锁(CountDownLatch)

    在讲闭锁之前,我们先来思考一个问题:在多线程环境下,主线程打印一句话,如何保证这句话最后(其他线程全部执行完毕)打印? 博主目前可以想到的实现方式有两种.一种是通过 join() 方法实现,另一种就是 ...

  8. reentrantlock非公平锁不会随机挂起线程?_【原创】Java并发编程系列16 | 公平锁与非公平锁...

    本文为何适原创并发编程系列第 16 篇,文末有本系列文章汇总. 上一篇提到重入锁 ReentrantLock 支持两种锁,公平锁与非公平锁.那么这篇文章就来介绍一下公平锁与非公平锁. 为什么需要公平锁 ...

  9. Python并发编程系列之多线程

    1 引言 上一篇博文详细总结了Python进程的用法,这一篇博文来所以说Python中线程的用法.实际上,程序的运行都是以线程为基本单位的,每一个进程中都至少有一个线程(主线程),线程又可以创建子线程 ...

最新文章

  1. as工程放到源码编译_UE4 Program 类型工程的限制和解决方法
  2. 国考中的电子信息类与计算机类,信息工程属于什么类-电子信息工程在公务员考试中属于计算机类吗 – 手机爱问...
  3. Linux系统调用--getrlimit()与setrlimit()函数详解
  4. yocto中文环境搭建
  5. Eclipse 报 “Exception in thread main java.lang.OutOfMemoryError: Java heap space ”错误的解决办法...
  6. Java实现前中后序线索化二叉树以及遍历
  7. 完整的POM文档内容
  8. MySQL中SELECT语句简单使用
  9. python3 collections数据类型模块
  10. 【OpenCV】OpenCV函数精讲之 -- 访问图像中的像素
  11. (102)FPGA面试题-如何选择FPGA型号?
  12. [设计模式-行为型]访问者模式(Vistor)
  13. iOS:UIWebView scrollView 的分页滑动问题
  14. 计算机网络笔记四 无线局域网
  15. 人工智能语音如何实现?
  16. 计算机系统维护要不要自动更新,电脑自动更新功能开启还是关闭,到底要不要关闭...
  17. linux的的符号,Linux 常见特殊符号
  18. 牛根生的“牛“,牛在哪里?
  19. 清理qmail邮件队列
  20. 输掉战役赢战争(博弈论的诡计)

热门文章

  1. “展厅三维全景”技术,将产品和企业文化以vr展示出来
  2. 移动应用测试篇(1)——移动应用的发展
  3. Web界面应用的测试内容
  4. 微信健身房小程序开发源码设计方案
  5. App Store Server API 实践总结
  6. C语言入门知识1(零基础新手适用)
  7. java与c的交互,java与c/c++之间的数据交互,java交互
  8. pgSQL查询语句ERROR: relation “XXX“ does not exist
  9. 树莓派Raspberry 4B+ 一篇快速搞定新版树莓派系统无屏幕初装+SSH连接+桌面显示
  10. Eclipse Maven clean后错误: 找不到或无法加载主类com.xxx.ShopApplication