上篇楼主说明了多线程中死锁产生的原因并抛出问题——死锁的解放方案,那么在本篇文章,楼主将引用一个KFC生产汉堡,顾客购买汉堡的过程来说明死锁解决方案及多线程的等待唤醒机制。

简单地用一幅图来说明KFC生产汉堡,顾客来消费的过程:

场景分析:

  1. 资源类:Hamburger
  2. 设置汉堡数据:SetThread(生产者)
  3. 获取汉堡数据:GetThread(消费者)
  4. 测试类:HamburgerTest
  5. 不同种类的线程(生产者、消费者)针对同一资源(汉堡)的操作
  6. 当汉堡有存货的时候,汉堡师傅不再生产,顾客可消费;反之,汉堡师傅生产,顾客不可消费
  7. 是否有线程安全问题?当然。楼主在《线程安全问题》那篇文章给出了判定方式,在该场景全部满足。

代码构建:类里面的i属性是楼主为了效果好一些特意加的,与本文要说明的问题无关;

  首先是资源类Hamburger.java,楼主这里为了模拟只简单的构造了3个字段,其中flag用来表示资源是否有数据。

 1 package com.jon.hamburger;2 3 public class Hamburger {4     private String name;//汉堡名称5     private double price;//汉堡价格6     private boolean flag;//汉堡是否有数据的标志,默认为false,表示没有数据7     public String getName() {8         return name;9     }
10     public void setName(String name) {
11         this.name = name;
12     }
13     public double getPrice() {
14         return price;
15     }
16     public void setPrice(double price) {
17         this.price = price;
18     }
19     public boolean isFlag() {
20         return flag;
21     }
22     public void setFlag(boolean flag) {
23         this.flag = flag;
24     }
25
26 }

  接着是生产者SetThread.java与GetThread.java,都需要实现Runnable接口。场景分析中的第7点已经说明,场景存在线程安全的问题,楼主在前篇文章已经说明,线程安全的问题可以通过加锁来进行解决,但是这里涉及到不同种类的线程,所以必须要满足2点:

  1. 不同种类的线程都要加锁
  2. 不同种类的线程加的锁必须是同一把

SetThread.java

 1 package com.jon.hamburger;2 3 public class SetThread implements Runnable {4     private Hamburger hamburger;5     private int i;6 7     public SetThread(Hamburger hamburger) {8         this.hamburger = hamburger;9     }
10     @Override
11     public void run() {
12         while (true) {//为了数据效果好一些,楼主加入了判断
13             synchronized (hamburger) {
14                 if(this.hamburger.isFlag()){//如果有存货
15                     try {
16                         hamburger.wait();//线程等待
17                     } catch (InterruptedException e) {
18                         e.printStackTrace();
19                     }
20                 }
21                 //如果没有存货,这模拟生产
22                 if (i % 2 == 0) {
23                     this.hamburger.setPrice(25.0);
24                     this.hamburger.setName("俊锅的汉堡");
25                 } else {
26                     this.hamburger.setPrice(26.0);
27                     this.hamburger.setName("大俊锅的汉堡");
28                 }
29                 this.hamburger.setFlag(true);//生产完成后更改标志
30                 hamburger.notify();//唤醒当前等待的线程
31                 i++;//只为数据效果好一些,无实际意义
32             }
33
34         }
35
36     }
37
38 }

GetThread.java

 1 package com.jon.hamburger;2 3 public class GetThread implements Runnable {4 5     private Hamburger hamburger;6     /**7      * 为了让同步锁使用同一个对象锁,这里通过构造方法进行传递8      * @param hamburger9      */
10     public GetThread(Hamburger hamburger){
11         this.hamburger = hamburger;
12     }
13     @Override
14     public void run() {
15         while(true){
16             synchronized (hamburger) {
17                 if(!this.hamburger.isFlag()){//如果没有存货,线程等待
18                     try {
19                         hamburger.wait();
20                     } catch (InterruptedException e) {
21                         e.printStackTrace();
22                     }
23                 }
24                 //如果有数据则进行输出
25                 System.out.println(this.hamburger.getName()+"-----"+this.hamburger.getPrice());
26                 this.hamburger.setFlag(false);//更改标志
27                 hamburger.notify();//唤醒线程
28             }
29         }
30
31     }
32
33 }

  可以看到两个线程类的run方法中都使用了sysnchronized进行了加锁,并使用同一个hamburger对象锁。

  再看测试类HamburgerTest.java及输出:

 1 package com.jon.hamburger;2 3 4 5 public class HamburgerTest {6 7 8     public static void main(String[] args) {9         Hamburger hamburger = new Hamburger();
10
11         SetThread st = new SetThread(hamburger);//通过构造方法传入共享资源数据hamburger
12         GetThread gt = new GetThread(hamburger);
13
14         Thread td1 = new Thread(st);
15         Thread td2 = new Thread(gt);
16
17         td1.start();
18         td2.start();
19
20     }
21
22 }

  测试类中,我们通过构造方法给SetThread和GetThread传入了同一个对象,以保证锁对象为同一把。

  输出结果,线程间不相互影响,同时都无NULL------0.0的情况输出:

  

1 俊锅的汉堡-----25.0
2 大俊锅的汉堡-----26.0
3 俊锅的汉堡-----25.0
4 大俊锅的汉堡-----26.0
5 俊锅的汉堡-----25.0
6 大俊锅的汉堡-----26.0
7 俊锅的汉堡-----25.0
8 大俊锅的汉堡-----26.0

代码分析:

  我们假设线程t2先抢到CPU的执行权,那么程序执行流程可用下图表示:

  根据程序代码分析也可见,由于线程之间相互等待产生的死锁问题也得以解决,解决方案就是通过唤醒。另外,文本楼主还使用了另一种方式,思路也差不多,示例代码与本文的示例代码放在一起,已上传到GitHub。

转载于:https://www.cnblogs.com/zhangyuliang/p/6782409.html

多线程等待唤醒机制之生产消费者模式相关推荐

  1. java基础提升(二):多线程、线程安全、线程状态、等待唤醒机制、线程池

    目录 一. 多线程 1.1并发与并行 1.2 线程与进程 1.3 创建线程类 1.3.1 方式一:继承Thread类 1.3.2 方式二:实现Runnable接口 1.3.3 Thread和Runna ...

  2. 24.多线程(等待唤醒机制,volatile,CAS 算法,线程池,定时器,设计模式)

    1.线程间的等待唤醒机制 Object 类中   void wait ()  在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待.         ...

  3. 进阶12 多线程、等待唤醒机制、线程池

    多线程 我们在之前,学习的程序在没有跳转语句的前提下,都是由上至下依次执行,那现在想要设计一个程序,边打游戏边听歌,怎么设计? 要解决上述问题,咱们得使用多进程或者多线程来解决. 并发与并行 并发:指 ...

  4. Java多线程02(线程安全、线程同步、等待唤醒机制)

    Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...

  5. 27_多线程_第27天(线程安全、线程同步、等待唤醒机制、单例设计模式)_讲义...

    今日内容介绍 1.多线程安全问题 2.等待唤醒机制 01线程操作共享数据的安全问题 *A:线程操作共享数据的安全问题如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程 ...

  6. 多线程之间的通信(等待唤醒机制、Lock 及其它线程的方法)

    一.多线程之间的通信. 就是多个线程在操作同一份数据, 但是操作的方法不同. 如: 对于同一个存储块,其中有两个存储位:name   sex, 现有两个线程,一个向其中存放数据,一个打印其中的数据. ...

  7. 多线程的等待唤醒机制

    一把锁一把钥匙..就是等待跟唤醒要在同一锁内 /* 多线程的等待唤醒机制 wait(); noyify();*/ class Pes {String name;String sex;boolean f ...

  8. 27_多线程_第27天(线程安全、线程同步、等待唤醒机制、单例设计模式)

    今日内容介绍 1.多线程安全问题 2.等待唤醒机制 01线程操作共享数据的安全问题 *A:线程操作共享数据的安全问题如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程 ...

  9. 锁 唤醒_Java笔记|等待唤醒机制

    等待唤醒案例分析:线程之间的通信 创建一个顾客线程(消费者):告知老板要的包子的种类和数量,调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待) 创建一个老板线程(生产者):花了5 ...

  10. Java线程等待唤醒机制(加深理解)

    今天看源码的时候遇到这样一个场景,某线程里面的逻辑需要等待异步处理结果返回后才能继续执行.或者说想要把一个异步的操作封装成一个同步的过程.这里就用到了线程等待唤醒机制,下面具体看一下. 等待唤醒机制示 ...

最新文章

  1. 1、java集合:java集合详解及类关系图
  2. golang中的接口
  3. 关于内存管理/set/get方法
  4. Spark Streaming的窗口操作
  5. cookie与session原理详解
  6. Linux下系统与硬件时钟管理
  7. 基础编程题目集 7-2 然后是几点 (15 分)
  8. 程序员为什么越老越贬值的厉害?
  9. 本页不但包含安全的内容,也包含不安全的内容
  10. 没有UITableViewController的UIRefreshControl
  11. python 自动抢红包_用二十行代码实现微信自动抢红包
  12. Java经典设计模式(3):十一种行为型模式(附实例和详解)
  13. xml转excel(Office XML转excel)
  14. FMI飞马网 | AI人工智能/大数据/Database/Linear Algebra/Python/机器学习/Hadoop 有哪些书籍?福利可下载!
  15. NOIP2017 普及 luogu3957 跳房子
  16. Weights Biases
  17. # 2021-01-13 #「FVWM」- 配置命令章节列表
  18. 【Rust日报】 2019-01-26
  19. 3dmax中的纹理材质有什么用
  20. Launchpad是什么?Launchpad使用教程

热门文章

  1. 【spring】spring动态代理和Spring_AOP
  2. Python中 sys.argv[]
  3. 5-7Linux 起源,与Unix的联系,与Windows的不同
  4. 求求你了,不要再自己实现这些逻辑了,开源工具类不香吗?
  5. k8s的精简版k3s安装
  6. Git 命令 checkout、reset、revert、reflog 、merge 使用介绍
  7. [Spring cloud 一步步实现广告系统] 5. 投放系统配置+启动+实体类
  8. 02.规划过程组表格-活动成本估算
  9. 这是用过的最差树形插件
  10. c++ 类的定义和使用