多线程等待唤醒机制之生产消费者模式
2017-03-12 14:59
519 查看
上篇楼主说明了多线程中死锁产生的原因并抛出问题——死锁的解放方案,那么在本篇文章,楼主将引用一个KFC生产汉堡,顾客购买汉堡的过程来说明死锁解决方案及多线程的等待唤醒机制。
简单地用一幅图来说明KFC生产汉堡,顾客来消费的过程:
场景分析:
资源类:Hamburger
设置汉堡数据:SetThread(生产者)
获取汉堡数据:GetThread(消费者)
测试类:HamburgerTest
不同种类的线程(生产者、消费者)针对同一资源(汉堡)的操作
当汉堡有存货的时候,汉堡师傅不再生产,顾客可消费;反之,汉堡师傅生产,顾客不可消费
是否有线程安全问题?当然。楼主在《线程安全问题》那篇文章给出了判定方式,在该场景全部满足。
代码构建:类里面的i属性是楼主为了效果好一些特意加的,与本文要说明的问题无关;
首先是资源类Hamburger.java,楼主这里为了模拟只简单的构造了3个字段,其中flag用来表示资源是否有数据。
接着是生产者SetThread.java与GetThread.java,都需要实现Runnable接口。场景分析中的第7点已经说明,场景存在线程安全的问题,楼主在前篇文章已经说明,线程安全的问题可以通过加锁来进行解决,但是这里涉及到不同种类的线程,所以必须要满足2点:
不同种类的线程都要加锁
不同种类的线程加的锁必须是同一把
SetThread.java
GetThread.java
可以看到两个线程类的run方法中都使用了sysnchronized进行了加锁,并使用同一个hamburger对象锁。
再看测试类HamburgerTest.java及输出:
测试类中,我们通过构造方法给SetThread和GetThread传入了同一个对象,以保证锁对象为同一把。
输出结果,线程间不相互影响,同时都无NULL------0.0的情况输出:
代码分析:
我们假设线程t2先抢到CPU的执行权,那么程序执行流程可用下图表示:
根据程序代码分析也可见,由于线程之间相互等待产生的死锁问题也得以解决,解决方案就是通过唤醒。另外,文本楼主还使用了另一种方式,思路也差不多,示例代码与本文的示例代码放在一起,已上传到GitHub。
本文示例代码地址:https://github.com/LJunChina/JavaResource 中的Hamburger
简单地用一幅图来说明KFC生产汉堡,顾客来消费的过程:
场景分析:
资源类:Hamburger
设置汉堡数据:SetThread(生产者)
获取汉堡数据:GetThread(消费者)
测试类:HamburgerTest
不同种类的线程(生产者、消费者)针对同一资源(汉堡)的操作
当汉堡有存货的时候,汉堡师傅不再生产,顾客可消费;反之,汉堡师傅生产,顾客不可消费
是否有线程安全问题?当然。楼主在《线程安全问题》那篇文章给出了判定方式,在该场景全部满足。
代码构建:类里面的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点:
不同种类的线程都要加锁
不同种类的线程加的锁必须是同一把
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) {// 4000 为了数据效果好一些,楼主加入了判断 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 hamburger 9 */ 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://github.com/LJunChina/JavaResource 中的Hamburger
相关文章推荐
- 多线程等待唤醒机制之生产消费者模式
- 多线程等待唤醒机制之生产消费者模式
- 多线程等待唤醒机制之生产消费者模式
- 多线程等待唤醒机制之生产消费者模式
- 多线程等待唤醒机制之生产消费者模式
- 【我的Java笔记】多线程_等待唤醒机制(生产消费者模式)
- 多线程——等待唤醒机制经典实例:生产者消费者模式
- 24 API-多线程(多线程(多线程JDK5Lock锁,生产者消费者等待唤醒机制,定时器),设计模式(设计原则,设计模式(简单工程,工厂方法,单例模式)
- 多线程 等待唤醒机制 生产者消费者 (Lock jdk1.5版)
- 黑马程序员_JavaSE基础14 之 线程间通信 等待唤醒机制 多生产多消费者 ...
- 多线程_生产者消费者之等待唤醒机制代码优化
- 多线程-生产者消费者之等待唤醒机制代码优化
- java多线程中的生产者与消费者之等待唤醒机制@Version2.0
- java多线程中的生产者与消费者之等待唤醒机制@Version1.0
- 多线程_生产者消费者之等待唤醒机制思路图解
- 多线程-等待唤醒机制经典案例-生产者消费者
- java多线程之 生产者和消费者 线程间通信 等待与唤醒机制
- 多线程__【线程间通信】【等待唤醒机制】【多生产多消费】【Lock&Condition接口】
- 多线程__【线程间通信】【等待唤醒机制】【多生产多消费】【Lock&Condition接口】
- 多线程_生产者消费者之等待唤醒机制代码实现