单个 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();}}没有出现任何问题,说明这种解决方案是可以的,也是目前看来最好的解决方案

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/53841.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Authing身份云-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 1. 暴力破解密码,造成用户信息泄露 2. 短信盗刷的安全问题,影响业务及导致用户投诉 3. 带来经济损失,尤其是后付费客户,风险巨大,造…

香港打工人√三天通过微软mos认证

在繁忙的香港,时间就是金钱,效率就是生命。作为一名香港的打工人,在这座竞争激烈的城市中,不断提升自我是保持竞争力的关键。最近,我完成了一项挑战:在短短三天内通过微软MOS认证大师。以下是我备考的经验分…

微调大模型:提高其代码修复能力的尝试

目录 一、作品背景: 二、作品目标: 三、作品技术方案: (1)标记化 (2)量化 (3) LoRA(低秩自适应)配置 (4)训练配置 (6)模型保存 四、作品效果: 一、作品背景: 随着大型模型技术的日益成…

SOMEIP_ETS_107: SD_Consider_Entries_Order

测试目的: 验证DUT在接收到包含两个条目的消息时,能够按照正确的顺序处理:首先是带有TTL 0的SubscribeEventgroup以删除订阅,然后是常规的SubscribeEventgroup以重新订阅。 描述 本测试用例旨在确保DUT能够正确处理订阅消息的顺…

Frida0C - Module相关API

亲爱的读者你们好啊,今天主要分享一下 frida 相关的学习文档,见文章最后一节。 Module var module Process.findModuleByAddress(Module.findBaseAddress("libc.so")) module.enumerateSymbols()enumerateSymbols 返回该 so 的符号表。 还…

Java项目: 基于SpringBoot+mybatis+maven医院管理系统(含源码+数据库+任务书+开题报告+毕业论文)

一、项目简介 本项目是一套基于SpringBootmybatismaven医院管理系统 包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过严格调试,eclipse或者idea 确保可以运行! 该系统功能完善、界面美观、操作简单、…

ffmpeg安装测试(支持cuda支持SRT)

文章目录 背景安装ffmpeg直接下载可执行文件选择版本选择对应系统版本下载测试Linux下安装 查看支持协议以及编码格式 常见错误缺少 libmvec.so.1LD_LIBRARY_PATH 错误 GPU加速测试SRT服务器搭建下载srs5.0源码解压安装配置启动 SRT推流测试SRT播放测试 背景 在音视频开发测试中…

视频监控管理平台LntonAIServer视频智能分析抖动检测算法应用场景

在视频监控系统中,视频画面的稳定性对于确保监控效果至关重要。抖动现象是指视频画面中存在不稳定或频繁晃动的情况,这可能会影响视频的清晰度和可读性。LntonAIServer通过引入抖动检测功能,帮助用户及时发现并解决视频流中的抖动问题&#x…

【Python】从基础到进阶(七):深入理解Python中的异常处理与调试技巧

🔥 个人主页:空白诗 文章目录 一、引言二、异常处理概述1. 什么是异常?2. 异常的捕获与处理 三、常见的异常类型四、自定义异常五、调试与日志记录1. 使用assert进行调试2. 使用日志记录 六、案例:文件操作与异常处理1. 需求分析2…

最新kubernetes的安装填坑之旅(新手篇)

Kubernetes(常简称为 K8s)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序,lz也不知道哪根脑经秀逗了,竟然妄挑战学习一下,结果折戟沉沙,被折腾的欲仙欲死,不过…

写的一致性问题之双删模式

文章目录 1、双删模式1.1、同步双删1.2、异步双删1.3、延时双删1.4、定时双删 在事务提交前后删除两次redis,会有性能问题 企业开发常用:延时双删、异步双删 1、双删模式 1.1、同步双删 实现思路:AOP 1.2、异步双删 在事务提交之后异步删除r…

redis群集的三种模式

目录 一、redis群集有三种模式 二、redis主从复制 2.1 概念 2.2 主从复制的作用 2.3 主从复制流程 三、搭建redis主从复制 四、redis哨兵模式 4.1 概念 4.2 哨兵模式原理: 4.3 哨兵模式的作用: 4.4 故障转移机制: 4.5 主节点的选举&#xff…

【计算机网络】电路交换、报文交换和分组交换——三种交换方式性能分析以及计算机网络的分类

【计算机网络】电路交换、电报交换、分组交换 目录 【计算机网络】电路交换、电报交换、分组交换1. 电路交换2. 电报交换3. 分组交换4. 基于分组交换~“虚电路交换”技术 【计算机网络】电路交换、报文交换和分组交换——三种交换方式性能分析电路交换性能分析报文交换性能分析…

C/C++:优选算法

一、双指针 1.1移动零 链接:283. 移动零 - 力扣(LeetCode) 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操…

[001-03-007].第07节:Redis中的事务

我的后端学习大纲 我的Redis学习大纲 1、Redis事务是什么: 1.可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化, 按顺序地串行化执行而不会被其他命令插入,不许加塞2.一个队列中,一次性、…

PLSQL-将一份excel数据导入到一张物理表(Oracle)

–>> 很简单~ 平时用惯了DBeaver,突然要用PLSQL Developer,确实很生疏。 –>> 我的场景,将一份.csv文件数据手动导入到Oracle下的一张物理表中去。 研究了半天,看网上说的可以用:Tools → ODBC Importer &…

WPF 手撸插件 八 依赖注入

本文内容大量参考了:https://www.cnblogs.com/Chary/p/11351457.html 而且这篇文章总结的非常好。 1、注意想使用Autofac,Autofac是一个轻量级、‌高性能的依赖注入(‌DI)‌框架,‌主要用于.NET应用程序的组件解耦和…

被低估的SQL

SQL是现代数据库管理系统中不可或缺的一部分。尽管它的使用已十分普遍,但在数据处理领域,SQL的某些功能和潜力仍然被许多人低估。接下来,小编将与您一起,探讨SQL的一些被忽视的特性,揭示它在数据管理中的真正实力。 1.…

红海云 × 滨湖国控集团 | 数智引领集团型国企人力资源数字化变革

合肥滨湖国有资本运营控股集团有限公司(以下简称“滨湖国控集团”)为合肥市包河区区属一级国企。滨湖国控集团作为安徽省市辖行政区中首个获得AA主体信用评级的区属国企,紧扣“三区”定位,聚焦“三位”追求 ,积极构筑金…

Python OpenCV精讲系列 - 高级图像处理技术(五)

💖💖⚡️⚡️专栏:Python OpenCV精讲⚡️⚡️💖💖 本专栏聚焦于Python结合OpenCV库进行计算机视觉开发的专业教程。通过系统化的课程设计,从基础概念入手,逐步深入到图像处理、特征检测、物体识…