当前位置: 首页 > news >正文

单个 java 虚拟机 生产者消费者

一、通过 java.lang.Object#wait(),java.lang.Object#notify,java.lang.Object#notifyAll来实现 生产者,消费者    

public abstract class Goods {protected String type;protected String goodName;protected int number;public abstract void produce();public abstract void consume();}public class Producer implements Runnable{private Goods goods;public Producer(Goods goods){this.goods = goods;}@Overridepublic void run() {while (true){goods.produce();}}}public class Consumer implements Runnable{private Goods goods;public Consumer (Goods goods){this.goods = goods;}@Overridepublic void run() {while (true){goods.consume();}}
}
         1.单个生产者,单个消费者
public class OneProducerOneConsumerGoods extends Goods{//false 表示货物是空的 可以继续生产private volatile boolean flag = false;public OneProducerOneConsumerGoods(String type , int beginNumber){this.type = type;this.number = beginNumber;}public synchronized void produce(){String name = Thread.currentThread().getName();//如果不为空  先等待if(flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}this.goodName = type + ":" + number;System.out.println(name + " 生产商品 " + goodName);number ++;flag = true;this.notify();}public synchronized void consume(){String name = Thread.currentThread().getName();//如果 flag == false 货物是空的,等待if(!flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(name + " 消费商品             " + goodName);flag = false;this.notify();}}
public class OneProducerOneConsumerTest {public static void main(String[] args) {Goods goods = new OneProducerOneConsumerGoods("面包" , 1);Producer producer = new Producer(goods);Consumer consumer = new Consumer(goods);Thread thread = new Thread(producer);Thread thread1 = new Thread(consumer);thread.start();thread1.start();}}这种情况没有出现数据安全问题,也没有出现死锁
 OneProducerOneConsumerGoods 是用于单个生产者单个消费者的,这里使用多个生产者和多个消费者,对 OneProducerOneConsumerGoods 进行操作看看会出现什么问题
/*** OneProducerOneConsumerGoods 是用于单个生产者单个消费者的* 这里使用多个生产者和多个消费者,对 OneProducerOneConsumerGoods 进行操作* 看看会出现什么问题*/
public class MultiProducerMultiConsumerTest {public static void main(String[] args) {Goods goods = new OneProducerOneConsumerGoods("面包" , 1);Producer producer = new Producer(goods);Consumer consumer = new Consumer(goods);Thread thread0 = new Thread(producer);Thread thread1 = new Thread(producer);Thread thread2 = new Thread(consumer);Thread thread3 = new Thread(consumer);thread0.start();thread1.start();thread2.start();thread3.start();}}

 出现了连续生产或者连续消费的现像,出现这种现像的原因是,当线程调用监视器的wait()方法的时候,不紧会放弃cpu的执行权,处于休眠状态,还会释放掉监视器,这样其他线程就可以进入到synchronized 同步代码块中。

        1.解释一下连续生产

        如果 Thread-0 判断 flag == false,生产一个,将flag修改为 true,然后cpu继续执行 Thread-0,下次判断  flag == true ,Thread-0 进入 wait() ,释放cpu的执行权 ,Thread-1 执行,判断  flag == true ,Thread-1 进入 wait(),释放cpu的执行权 ,这时Thread-0 Thread-1 都进入wait(),Thread-2 开始执行,判断  flag == true,进行消费,然后将flag 修改为 false,Thread-2 消费完了,会通过notify()唤醒一个线程,这时候不管时Thread-2 继续执行还是 Thread-3 执行都会进入到 wait(),Thread-2 通过notify() 唤醒 Thread-0 Thread-1 中的一个,假如唤醒了 Thread-0 他不会再去判断 flag 而是直接往下执行,去生产,生产完Thread-0 通过notify() 唤醒一个线程,这个时候如果  Thread-1 被唤醒,他也不会再去判断 flag ,而是直接往下执行,进行生产,这样就发生了连续生产,如果碰巧 Thread-0 和 Thread-1 连续的相互唤醒,就会出现长时间的连续生产

        2.解释一下连续消费

        如果 Thread-2 判断 flag == false 进入wait() ,Thread-3 开始执行,也进入到wait(),这时候Thread-2 Thread-3 都处于wait(),这是Thread-0 开始执行判断flag == false,进行生产 ,将 flag 修改为 true,调用监视器的 notify() 唤醒 Thread-2 Thread-3 中的一个,假如唤醒了Thread-2 ,他不会再去判断 flag 而是直接往下执行去消费,消费完将flag修改为false,调用监视器的 notify() 唤醒一个线程,如果这时正好唤醒了 Thread-3,他不会再判断 flag ,直接往下执行,去消费,这样就发生了连续的消费,如果碰巧 Thread-2 Thread-3 连续的相互唤醒,就会出现长时间的连续消费

     3.发生这两种现像的原因是,不管生产者还是消费者,如果他们 wait() 之后被唤醒,不会再判断 flag ,导致在不该生产的时候进行了生产,不该消费的时候进行了消费

这里对OneProducerOneConsumerGoods 进行修改,被唤醒之后再次判断 flag 

public class MultiProducerMultiConsumerGoods extends Goods{//false 表示货物是空的 可以继续生产private volatile boolean flag = false;public MultiProducerMultiConsumerGoods(String type , int beginNumber){this.type = type;this.number = beginNumber;}public synchronized void produce(){String name = Thread.currentThread().getName();//这里用while循环,如果wait() 被唤醒后,再次判断 flag//如果不为空  先等待while (flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}this.goodName = type + ":" + number;System.out.println(name + " 生产商品 " + goodName);number ++;flag = true;this.notify();}public synchronized void consume(){String name = Thread.currentThread().getName();//这里用while循环,如果wait() 被唤醒后,再次判断 flag//如果 flag == false 货物是空的,等待while (!flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(name + " 消费商品             " + goodName);flag = false;this.notify();}}
public class MultiProducerMultiConsumerTest1 {public static void main(String[] args) {Goods goods = new MultiProducerMultiConsumerGoods("面包" , 1);Producer producer = new Producer(goods);Consumer consumer = new Consumer(goods);Thread thread0 = new Thread(producer);Thread thread1 = new Thread(producer);Thread thread2 = new Thread(consumer);Thread thread3 = new Thread(consumer);thread0.start();thread1.start();thread2.start();thread3.start();}}发生了死锁的现像

这里举例子再解释一下死锁发生的原因,如果 Thread-2 判断 flag == false 进入wait() ,Thread-3 开始执行,也进入到wait(),这时候Thread-2 Thread-3 都处于wait(),这是Thread-0 开始执行判断flag == false,进行生产 ,将 flag 修改为 true,调用监视器的 notify() 唤醒 Thread-2 Thread-3 中的一个,这时 Thread-0 继续执行,判断 flag == true ,进入wait() ,  然后 Thread-1 进行执行,判断 flag == true ,进入wait() ,这时 Thread-0 Thread-1 都处于wait() , 但是Thread-2 Thread-3 中有一个之前被 Thread-0 唤醒,如果Thread-2 被唤醒,重新判断 flag == true,进行消费,消费完了之后调用监视器的 notify() 唤醒一个线程,正好唤醒 Thread-3,这时 Thread-3 重新判断 flag == true,进入wait(), Thread-2 如果继续执行 也会判断 flag == true,进入wait(),这样 四个线程就全部进入了wait(),形成了死锁。

这里总结死锁的原因,是因为调用监视器的 notify() 只能唤醒一个线程,如果正好唤醒的是本方的一个线程,那么重新判断 flag ,也会进入到 wait(),导致所有线程都wait(),要解决这个问题,就要在唤醒的时候,至少唤醒一个对方的线程,这样重新判断 flag 才不会直接进入 wait().

我们对上面的代码进行修改,每次唤醒都唤醒全部的线程,这样对方的线程也会被唤醒,继续往下执行,形成不断的唤醒对方的效果,就不会死锁了

public class MultiProducerMultiConsumerGoods1 extends Goods{//false 表示货物是空的 可以继续生产private volatile boolean flag = false;public MultiProducerMultiConsumerGoods1(String type , int beginNumber){this.type = type;this.number = beginNumber;}public synchronized void produce(){String name = Thread.currentThread().getName();//如果不为空  先等待while (flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}this.goodName = type + ":" + number;System.out.println(name + " 生产商品 " + goodName);number ++;flag = true;//这里唤醒全部的线程this.notifyAll();}public synchronized void consume(){String name = Thread.currentThread().getName();//如果 flag == false 货物是空的,等待while (!flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(name + " 消费商品             " + goodName);flag = false;//这里唤醒全部的线程this.notifyAll();}}
public class MultiProducerMultiConsumerTest2 {public static void main(String[] args) {Goods goods = new MultiProducerMultiConsumerGoods1("面包" , 1);Producer producer = new Producer(goods);Consumer consumer = new Consumer(goods);Thread thread0 = new Thread(producer);Thread thread1 = new Thread(producer);Thread thread2 = new Thread(consumer);Thread thread3 = new Thread(consumer);thread0.start();thread1.start();thread2.start();thread3.start();}}没有发生 连续生产 连续消费 死锁 等问题

这里可能会有人这样想,一下子把所有的线程都唤醒了,这样对cpu的线程资源和计算资源太浪费了吧,可不可以在多生产者多消费者的情况下,只唤醒一个对方线程,而不是把所有的线程都唤醒呢,这样的想法确实很好,我们尝试可不可以用两个锁,一个专门用于生产线程的同步,一个专门用于消费线程的同步,我先把这个想法的代码写出来,能不能用再说

public class MultiProducerMultiConsumerGoods2 extends Goods{//false 表示货物是空的 可以继续生产private volatile boolean flag = false;//本来想法是专门限制生产的锁private Object produceLock = new Object();//本来想法是专门限制消费的锁private Object consumeLock = new Object();public MultiProducerMultiConsumerGoods2(String type , int beginNumber){this.type = type;this.number = beginNumber;}public void produce(){String name = Thread.currentThread().getName();//生产线程获取生产锁,然后进入进行生产或者 等待synchronized(this.produceLock) {//如果不为空  先等待while (flag) {try {this.produceLock.wait();} catch (InterruptedException e) {e.printStackTrace();}}this.goodName = type + ":" + number;System.out.println(name + " 生产商品 " + goodName);number++;flag = true;//本来想法是生产完了,通过消费锁唤醒一个消费线程,避免唤醒本方线程 //和 对方过多的线程this.consumeLock.notify();}}public void consume(){String name = Thread.currentThread().getName();//消费线程获取消费锁,然后进入进行消费或者 等待synchronized(this.consumeLock) {//如果 flag == false 货物是空的,等待while (!flag) {try {this.consumeLock.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(name + " 消费商品             " + goodName);flag = false;//本来想法是消费完了,通过生产锁唤醒一个生产线程,避免唤醒本方线程 //和 对方过多的线程this.produceLock.notify();}}}
public class MultiProducerMultiConsumerTest3 {public static void main(String[] args) {Goods goods = new MultiProducerMultiConsumerGoods2("面包" , 1);Producer producer = new Producer(goods);Consumer consumer = new Consumer(goods);Thread thread0 = new Thread(producer);Thread thread1 = new Thread(producer);Thread thread2 = new Thread(consumer);Thread thread3 = new Thread(consumer);thread0.start();thread1.start();thread2.start();thread3.start();}}Thread-0 生产商品 面包:1
Thread-3 消费商品             面包:1
Exception in thread "Thread-0" Exception in thread "Thread-3" java.lang.IllegalMonitorStateExceptionat java.lang.Object.notify(Native Method)at com.fll.test.multi_thread.producer_consumer.MultiProducerMultiConsumerGoods2.produce(MultiProducerMultiConsumerGoods2.java:40)at com.fll.test.multi_thread.producer_consumer.Producer.run(Producer.java:15)at java.lang.Thread.run(Thread.java:748)
java.lang.IllegalMonitorStateExceptionat java.lang.Object.notify(Native Method)at com.fll.test.multi_thread.producer_consumer.MultiProducerMultiConsumerGoods2.consume(MultiProducerMultiConsumerGoods2.java:66)at com.fll.test.multi_thread.producer_consumer.Consumer.run(Consumer.java:15)at java.lang.Thread.run(Thread.java:748)我们可以看到报出了 IllegalMonitorStateException , 
因为一个synchronized代码块只能指定一个监视器,
并且当一个线程在获取到监视器进入同步代码块里面的时候,
只能调用所进入的synchronized代码块所指定的监视器的 wait() notify() notifyAll(), 
所以当生产线程获取到生产监视器 produceLock,进入synchronized 代码块,
生产完了调用 consumeLock 的 notify() 的时候就会报错

看来通过 synchronized 无法实现只唤醒对方线程的操作,但是jdk1.5 出来新的API,Lock 锁提供了相应的实现,我们先来看看代码怎么实现
 

public class MultiProducerMultiConsumerGoods3 extends Goods{//false 表示货物是空的 可以继续生产private volatile boolean flag = false;private Lock lock = new ReentrantLock();// 同一个Lock锁对象可以创建多个与其关联的 监视器对象//专门限制生产的 监视器private Condition produceCondition = lock.newCondition();//专门限制消费的 监视器private Condition consumeCondition = lock.newCondition();public MultiProducerMultiConsumerGoods3(String type , int beginNumber){this.type = type;this.number = beginNumber;}public void produce(){String name = Thread.currentThread().getName();//生产线程获取生产锁,然后进入进行生产或者 等待lock.lock();try {//如果不为空  先等待while (flag) {try {//await() 方法和 Object 的wait() 都会释放cpu执行权//并且会释放锁this.produceCondition.await();} catch (InterruptedException e) {e.printStackTrace();}}this.goodName = type + ":" + number;System.out.println(name + " 生产商品 " + goodName);number++;flag = true;//生产完了,通过消费 监视器 唤醒一个消费线程,避免唤醒本方线程 和 对方过多的线程this.consumeCondition.signal();}finally {lock.unlock();}}public void consume(){String name = Thread.currentThread().getName();//消费线程获取消费锁,然后进入进行消费或者 等待lock.lock();try {//如果 flag == false 货物是空的,等待while (!flag) {try {//await() 方法和 Object 的wait() 都会释放cpu执行权//并且会释放锁this.consumeCondition.await();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(name + " 消费商品             " + goodName);flag = false;//消费完了,通过生产 监视器 醒一个生产线程,避免唤醒本方线程 和 对方过多的线程this.produceCondition.signal();}finally {lock.unlock();}}}
public class MultiProducerMultiConsumerTest4 {public static void main(String[] args) {Goods goods = new MultiProducerMultiConsumerGoods3("面包" , 1);Producer producer = new Producer(goods);Consumer consumer = new Consumer(goods);Thread thread0 = new Thread(producer);Thread thread1 = new Thread(producer);Thread thread2 = new Thread(consumer);Thread thread3 = new Thread(consumer);thread0.start();thread1.start();thread2.start();thread3.start();}}没有出现任何问题,说明这种解决方案是可以的,也是目前看来最好的解决方案

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 《OpenCV计算机视觉》—— 对图片进行旋转的两种方法
  • 3D云渲染农场为何怎么贵?主要消耗成本介绍
  • 什么是野指针和悬空指针?
  • NVIDIA驱动学习
  • 【即时通讯】轮询方式实现
  • Android 内置应用裁剪
  • 基于python实现Modis数据的检索与下载
  • React第三章(tsx语法入门 )
  • 移远通信高端5G智能模组SG560D-NA率先通过PTCRB认证
  • 配置WSL(单纯记录
  • Hive任务优化参数整理
  • 【笔记】绪论 轨道交通材料及其加工工艺
  • Version ‘18.19.0‘ not found - try `nvm ls-remote` to browse available versions.
  • torch.stack()方法在数据集构造中的应用
  • 【Rust】008-常用集合
  • 深入了解以太坊
  • 【翻译】babel对TC39装饰器草案的实现
  • Android Studio:GIT提交项目到远程仓库
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 分布式事物理论与实践
  • 高性能JavaScript阅读简记(三)
  • 开源SQL-on-Hadoop系统一览
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 深度解析利用ES6进行Promise封装总结
  • 协程
  • Android开发者必备:推荐一款助力开发的开源APP
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (3) cmake编译多个cpp文件
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (PHP)设置修改 Apache 文件根目录 (Document Root)(转帖)
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (分布式缓存)Redis持久化
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (十一)c52学习之旅-动态数码管
  • (四) Graphivz 颜色选择
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (五)关系数据库标准语言SQL
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • .net CHARTING图表控件下载地址
  • /etc/sudoers (root权限管理)
  • []常用AT命令解释()
  • []指针
  • [20170705]diff比较执行结果的内容.txt
  • [ACTF2020 新生赛]Upload 1
  • [AI Embedchain] 开始使用 - 全栈
  • [AIGC] 使用Curl进行网络请求的常见用法
  • [BZOJ 4129]Haruna’s Breakfast(树上带修改莫队)
  • [C/C++入门][字符与ASCII码]6、用代码来转换字符与它的ASCII码
  • [C++核心编程](四):类和对象——封装