生产者(厨师做面条)消费者(吃货吃面条)模式synchronized 法和Lock+Condition法(不使用集合),代码详解
1. synchronized + wait + notifyAll
- synchronized:包裹着同步代码块,代码块执行完释放锁
- wait:线程挂起,失去锁
- notifyAll:唤醒同个对象的wait,此时wait所在线程处于争抢线程执行权的状态,如果抢到了,让wait所在的线程继续往下执行(wait代码的下一行代码开始)
- 为什么厨师(或者吃货)执行完synchronized同步代码块后(厨师和吃货的锁已经开启,都蓄势待发抢执行权)一定会继续执行第二次,而不会被已经唤醒的吃货(或者厨师)抢了执行权?因为厨师(或者吃货)处于while循环中,执行得更快。
以下代码证明上述四点:
2. Lock+Condition+await+signalAll
==========================================================
1. synchronized + wait + notifyAll
测试类
public class ThreadDemo {public static void main(String[] args) {//创建线程的对象Cook c = new Cook();Foodie f = new Foodie();//给线程设置名字c.setName("厨师");f.setName("吃货");//开启线程c.start();f.start();}
}
厨师
public class Cook extends Thread{@Overridepublic void run() {int i = 0; //记录厨师第几次进锁while (true){//线程csynchronized (Desk.lock){i++; //比如:厨师第1次进锁System.out.println(getName() +"第"+ i+"次进锁");if(Desk.count == 0){break;}else{//判断桌子上是否有食物if(Desk.foodFlag == 1){System.out.println("cook waiting");//如果有,就等待try {Desk.lock.wait();//wait执行后下面代码不会执行,直到被notify,然后获得执行权后,//才会在控制台打印厨师被唤醒了System.out.println(getName()+"被唤醒了");} catch (InterruptedException e) {e.printStackTrace();}}else{//如果没有,就制作食物System.out.println("厨师做了一碗面条");//修改桌子上的食物状态Desk.foodFlag = 1;//叫醒等待的消费者开吃Desk.lock.notifyAll();try {//尽管notifyAll唤醒了wait中的吃货,但是厨师还//是占用着锁,即使sleep了1秒也不会被蓄势待发的吃货抢到执行权Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}}}
}
吃货
public class Foodie extends Thread{@Overridepublic void run() {int i = 0; //记录吃货第几次进锁while(true){//线程fsynchronized (Desk.lock){i++; //比如:吃货第1次进锁System.out.println(getName() +"第"+ i+"次进锁");if(Desk.count == 0){break;}else{//先判断桌子上是否有面条if(Desk.foodFlag == 0){System.out.println("foodie waiting");//如果没有,就等待try {Desk.lock.wait();//让当前线程跟锁进行绑定//wait执行后下面代码不会执行,直到被notify,然后获得执行权后,//才会在控制台打印吃货被唤醒了System.out.println(getName()+"被唤醒了");} catch (InterruptedException e) {e.printStackTrace();}}else{//把吃的总数-1Desk.count--;//如果有,就开吃System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");//吃完之后,唤醒厨师继续做Desk.lock.notifyAll();//修改桌子的状态Desk.foodFlag = 0;try {//尽管notifyAll唤醒了wait中的厨师,但是吃货还//是占用着锁,即使sleep了1秒也不会被蓄势待发的厨师抢到执行权Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}}}
}
桌子 为了方便设置吃货能吃的面条为count=3
public class Desk {//是否有面条 0:没有面条 1:有面条public static int foodFlag = 0;//总个数public static int count = 3;//锁对象public static Object lock = new Object();
}
执行ThreadDemo 测试类,结果如下:
为什么厨师先执行?那是因为测试类中
//开启线程
c.start();
f.start();
厨师先start
厨师第1次进锁
厨师做了一碗面条 //此时厨师线程的synchronized同步代码块已经执行完毕!//锁已经开启,此时厨师和吃货是同等概率争取执行权的,//但是由于厨师处于while(true)死循环中,会比吃货更快//抢到执行权,所以厨师第2次进锁。下面代码会证明。//厨师唤醒吃货,第一次执行吃货还没有waiting
厨师第2次进锁
cook waiting //厨师释放锁,处于waiting中,
吃货第1次进锁
吃货在吃面条,还能再吃1碗!!! //吃完面条,同时唤醒厨师,厨师处于争抢执行权状态,//但是抢不过现在的吃货,因为吃货处于死循环中,执行更快。
吃货第2次进锁 //同理,吃货虽然执行完了同步代码块,但是处于死循环中
foodie waiting //执行到wait代码,让吃货处于waiting状态
厨师被唤醒了 //上一次厨师waiting被唤醒后,抢到了执行权继续执行下一行代码
厨师第3次进锁
厨师做了一碗面条
厨师第4次进锁
cook waiting
吃货被唤醒了
吃货第3次进锁
吃货在吃面条,还能再吃0碗!!!
吃货第4次进锁
厨师被唤醒了
厨师第5次进锁Process finished with exit code 0
证明死循环的线程更快,直接在厨师的同步代码块前增加一一行代码,或者sleep,延迟一下时间,看吃货有没有抢先一步。
厨师增加一行代码
public class Cook extends Thread{@Overridepublic void run() {int i = 0;System.out.println(getName()+"蓄势待发,想要进锁");while (true){//线程csynchronized (Desk.lock){i++;System.out.println(getName() +"第"+ i+"次进锁");if(Desk.count == 0){break;}else{//判断桌子上是否有食物if(Desk.foodFlag == 1){System.out.println("cook waiting");//如果有,就等待try {Desk.lock.wait();System.out.println(getName()+"被唤醒了");} catch (InterruptedException e) {e.printStackTrace();}}else{//如果没有,就制作食物System.out.println("厨师做了一碗面条");//修改桌子上的食物状态Desk.foodFlag = 1;//叫醒等待的消费者开吃Desk.lock.notifyAll();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}}}
}
运行结果:
厨师蓄势待发,想要进锁 测试类中,厨师先start
吃货第1次进锁 //由于打印了语句,厨师耽误了时间,给吃货抢先一步获得了执行权
foodie waiting
厨师第1次进锁
厨师做了一碗面条
厨师蓄势待发,想要进锁 //厨师蓄势待发,想进入第二次循环,却因为打印语句//浪费了时间,被吃货抢到了执行权,如下
吃货被唤醒了
吃货第2次进锁
吃货在吃面条,还能再吃1碗!!!
吃货第3次进锁
foodie waiting
厨师第2次进锁
厨师做了一碗面条
吃货被唤醒了
厨师蓄势待发,想要进锁
吃货第4次进锁
吃货在吃面条,还能再吃0碗!!!
吃货第5次进锁
厨师第3次进锁Process finished with exit code 0
=========================================================
2. Lock+Condition+await+signalAll
测试类
package com.cfj.lockstest;public class ThreadDemo {public static void main(String[] args) {//创建线程的对象Cook c = new Cook();Foodie f = new Foodie();//给线程设置名字c.setName("厨师");f.setName("吃货");//开启线程c.start();f.start();}
}
厨师
package com.cfj.lockstest;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Cook extends Thread {@Overridepublic void run() {int i = 0;while (true) {//线程cSystem.out.println(getName() + "蓄势待发,想要进锁");//synchronized (Desk.lock){//lock.lock();Desk.lock.lock();try {i++;System.out.println(getName() + "第" + i + "次进锁");if (Desk.count == 0) {break;} else {//判断桌子上是否有食物if (Desk.foodFlag == 1) {System.out.println("cook waiting");//如果有,就等待try {//Desk.lock.wait();Desk.condition.await();System.out.println(getName() + "被唤醒了");} catch (InterruptedException e) {e.printStackTrace();}} else {//如果没有,就制作食物System.out.println("厨师做了一碗面条");//修改桌子上的食物状态Desk.foodFlag = 1;//叫醒等待的消费者开吃//Desk.lock.notifyAll();Desk.condition.signalAll();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}} catch (Exception e) {e.printStackTrace();} finally {Desk.lock.unlock();}
// }}}
}
吃货
package com.cfj.lockstest;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Foodie extends Thread {static Lock lock = new ReentrantLock();@Overridepublic void run() {/** 1. 循环* 2. 同步代码块* 3. 判断共享数据是否到了末尾(到了末尾)* 4. 判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)* */int i = 0;while (true) {//线程f//synchronized (Desk.lock){//lock.lock();Desk.lock.lock();try {i++;System.out.println(getName() + "第" + i + "次进锁");if (Desk.count == 0) {break;} else {//先判断桌子上是否有面条if (Desk.foodFlag == 0) {System.out.println("foodie waiting");//如果没有,就等待try {//Desk.lock.wait();//让当前线程跟锁进行绑定Desk.condition.await();System.out.println(getName() + "被唤醒了");} catch (InterruptedException e) {e.printStackTrace();}} else {//把吃的总数-1Desk.count--;//如果有,就开吃System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");//吃完之后,唤醒厨师继续做//Desk.lock.notifyAll();Desk.condition.signalAll();//修改桌子的状态Desk.foodFlag = 0;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}} catch (Exception e) {e.printStackTrace();} finally {Desk.lock.unlock();}
// }}}
}
桌子
package com.cfj.lockstest;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Desk {//是否有面条 0:没有面条 1:有面条public static int foodFlag = 0;//总个数public static int count = 2;//锁对象
// public static Object lock = new Object();public static Lock lock = new ReentrantLock();public static Condition condition = lock.newCondition();
}
运行结果:
厨师蓄势待发,想要进锁
吃货第1次进锁
foodie waiting
厨师第1次进锁
厨师做了一碗面条
厨师蓄势待发,想要进锁
吃货被唤醒了
吃货第2次进锁
吃货在吃面条,还能再吃1碗!!!
吃货第3次进锁
foodie waiting
厨师第2次进锁
厨师做了一碗面条
厨师蓄势待发,想要进锁
吃货被唤醒了
吃货第4次进锁
吃货在吃面条,还能再吃0碗!!!
吃货第5次进锁
厨师第3次进锁
生产者(厨师做面条)消费者(吃货吃面条)模式synchronized 法和Lock+Condition法(不使用集合),代码详解相关推荐
- 使用python编写三个吃货吃馒头与一个伙夫生产馒头(线程与锁)
一个伙夫蒸馒头(生产者,售票),要求蒸够10个,等待,并唤醒吃馒头的人 三个吃货吃馒头(消费者,买票),要求同时吃,当任何一个人发现没馒头了,唤醒伙夫. 生产者是一堆线程,消费者是另一堆线程,内存缓冲 ...
- sketch如何做设计稿交互_《动静之美——Sketch移动UI与交互动效设计详解》历程...
随着移动互联网的迅速崛起,对移动产品界面的设计质量和迭代速度都有了更高的要求,市面上开始涌现出一批专门针对移动UI设计的软件,Sketch便是其中的佼佼者.Sketch因其强大的功能以及极低的入门门槛 ...
- 生产者消费者问题-代码详解(Java多线程)
你好我是辰兮,很高兴你能来阅读,本篇是整理了Java多线程中常见的生产者消费者问题,也是面试手写代码的高频问题,分享获取新知,大家共同进步! 1.JAVA基础面试常考问题 : JAVA面试基础常考题汇 ...
- 生产者消费者模型——C语言代码详解
概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者 ...
- 五款电动车充电器原理图(重点图2分析过程UC3842的VCC启动电压和稳定供电电压说明,12V由稳压二极管得到做外围电源LM358比较器可被控制输出12V 和0V切换)UC3842电源管理芯片详解
经过和精通开关电源设计中TL431反馈输出电压通道,分压电路(R7,P,W2组成)比较,第二图的TL431参考极电路是正确的,最下面接的输出的42V的正极.D8和W1在一起可以调整浮充的拐点电流(不太 ...
- 【Python】Python一行代码能做什么,30个实用案例代码详解
Python语法简洁,能够用一行代码实现很多有趣的功能,这次来整理30个常见的Python一行代码集合. 1.转置矩阵 old_list = [[1, 2, 3], [3, 4, 6], [5, 6, ...
- 做折线图_python的visvis库做折线图(line.py)代码详解
1 说明: ===== 1.1 visvis库:是Python视觉库,非常强大. 1.2 visvis库:今天讲折线图(line.py)的代码讲解. 1.3 安装: pip install visvi ...
- Python一行代码能做什么,30个实用案例代码详解
Python语法简洁,能够用一行代码实现很多有趣的功能,这次来整理30个常见的Python一行代码集合. 1.转置矩阵 old_list = [[1, 2, 3], [3, 4, 6], [5, 6, ...
- java 读者写者_Java实现生产者消费者问题与读者写者问题详解
1.生产者消费者问题 生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品.解决生产者/消费者问题的方法可分为两 ...
最新文章
- Spark发布1.3.0版本
- 如何统计各个班级的相关数据(如班级排名、最高分等)呢?
- C++知识点34——动态内存与智能指针
- nfs failed, reason given by server: Permission denied的离奇解决
- mean技术栈 linux,“MEAN”技术栈开发web应用
- ftp+linux+使用webdav,群晖-win/mac/nfs ftp tftp webdav文件服务的概念及设置
- 文字转语音文件的两种方法
- 5个球放入3个箱子_国内5个经典的美食小吃,吃过3个以上算厉害,你吃过几个?...
- mysql relay log 配置_mysql relay log参数汇总
- 简单的跑马灯效果(轮播图)
- 【体系结构】Oracle如何保证提交的数据不丢失
- java jlabel 字体大小_java – 如何在调整大小时更改JLabel字体大小以填充JPanel可用空间?...
- Yum介绍与常见用法
- C++ 非类型的模板参数
- SqlServer数据库(可疑)解决办法4种
- 联想服务器双系统安装,联想Y700一键安装双系统教程
- 汇编“从键盘输入一串字符,分别统计其中字母、数字和其
- 关于Linux的介绍与安装
- 安卓android按宽/高缩放图片到指定大小并进行裁剪得到中间部分图片
- java判断字符串长度_java判断中文字符串长度的简单实例