Java多线程生产者消费者说明等待唤醒机制问题和虚假唤醒问题
2017-03-07 15:34
513 查看
不用等待唤醒机制实现的生产者与消费者
代码
package com.hust.juc; /* * 生产者和消费者案例 */ public class TestProductorAndConsumer { public static void main(String[] args) { Clerk clerk = new Clerk(); Productor pro = new Productor(clerk); Consumer cus = new Consumer(clerk); new Thread(pro, "生产者 A").start(); new Thread(cus, "消费者 B").start(); /* * new Thread(pro, "生产者 C").start(); new Thread(cus, "消费者 D").start(); */ } } // 店员 class Clerk { private int product = 0; // 进货 public synchronized void get() {// 循环次数:0 if (product >= 1) { System.out.println("产品已满!"); /*try { this.wait(); } catch (InterruptedException e) { }*/ } else { System.out.println(Thread.currentThread().getName() + " : " + ++product); //this.notifyAll(); } } // 卖货 public synchronized void sale() {// product = 0; 循环次数:0 if (product <= 0) { System.out.println("缺货!"); /*try { this.wait(); } catch (InterruptedException e) { }*/ } else { System.out.println(Thread.currentThread().getName() + " : " + --product); //this.notifyAll(); } } } // 生产者 class Productor implements Runnable { private Clerk clerk; public Productor(Clerk clerk) { this.clerk = clerk; } @Override public void run() { for (int i = 0; i < 20; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { } clerk.get(); } } } // 消费者 class Consumer implements Runnable { private Clerk clerk; public Consumer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { for (int i = 0; i < 20; i++) { clerk.sale(); } } }
运行结果
只要线程得到了CPU就会运行,缺货也会一直去缺货,很不科学,所以这个时候我们得用等待唤醒机制。
使用等待唤醒机制
代码
就是把上面的代码中await和notify的注释打开运行结果
这个时候会发现都运行完了,但是程序并没有结束,这就是等待唤醒机制遗留问题。
解决等待唤醒机制的问题
我们分析一下,很容易想到是因为,await被唤醒之后是从await的地方继续执行,那么直接从else出走掉了,假如那是消费者的最后一轮循环,那么那个最后一轮循环的线程就结束了,生产者还有一轮或者多伦循环没有结束,生产者线程生产出的商品就没有线程去消费就会一直等待。代码去掉else就解决了这个问题
package com.hust.juc; /* * 生产者和消费者案例 */ public class TestProductorAndConsumer { public static void main(String[] args) { Clerk clerk = new Clerk(); Productor pro = new Productor(clerk); Consumer cus = new Consumer(clerk); new Thread(pro, "生产者 A").start(); new Thread(cus, "消费者 B").start(); /* * new Thread(pro, "生产者 C").start(); new Thread(cu 4000 s, "消费者 D").start(); */ } } // 店员 class Clerk { private int product = 0; // 进货 public synchronized void get() {// 循环次数:0 if (product >= 1) { System.out.println("产品已满!"); try { this.wait(); } catch (InterruptedException e) { } } System.out .println(Thread.currentThread().getName() + " : " + ++product); this.notifyAll(); } // 卖货 public synchronized void sale() {// product = 0; 循环次数:0 if (product <= 0) { System.out.println("缺货!"); try { this.wait(); } catch (InterruptedException e) { } } System.out .println(Thread.currentThread().getName() + " : " + --product); this.notifyAll(); } } // 生产者 class Productor implements Runnable { private Clerk clerk; public Productor(Clerk clerk) { this.clerk = clerk; } @Override public void run() { for (int i = 0; i < 20; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { } clerk.get(); } } } // 消费者 class Consumer implements Runnable { private Clerk clerk; public Consumer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { for (int i = 0; i < 20; i++) { clerk.sale(); } } }
多线程下的虚假唤醒
说白了其实这还是“单线程“,因为只有一个线程生产一个线程消费,如果现在有两个线程去生产,两个线程去消费,那么就会出现虚假唤醒的情况。虚假唤醒代码
把上面的代码中CD线程注释掉的打开就行。运行截图
虚假唤醒问题分析
仔细考虑一下,假如现在消费者线程C执行,此时没有商品,消费者线程C等待,这个时候消费者线程D也拿到了CPU,也是没有商品的状态,也等待。这个时候一个生产者生产出了一个商品,此时唤醒了两个等待的消费者,往下执行,那么很自然就产生了-1等负数的情况。解决办法就是把wait放在循环里
package com.hust.juc; /* * 生产者和消费者案例 */ public class TestProductorAndConsumer { public static void main(String[] args) { Clerk clerk = new Clerk(); Productor pro = new Productor(clerk); Consumer cus = new Consumer(clerk); new Thread(pro, "生产者 A").start(); new Thread(cus, "消费者 B").start(); new Thread(pro, "生产者 C").start(); new Thread(cus, "消费者 D").start(); } } // 店员 class Clerk { private int product = 0; // 进货 public synchronized void get() {// 循环次数:0 while (product >= 1) { System.out.println("产品已满!"); try { this.wait(); } catch (InterruptedException e) { } } System.out .println(Thread.currentThread().getName() + " : " + ++product); this.notifyAll(); } // 卖货 public synchronized void sale() {// product = 0; 循环次数:0 while (product <= 0) { System.out.println("缺货!"); try { this.wait(); } catch (InterruptedException e) { } } System.out .println(Thread.currentThread().getName() + " : " + --product); this.notifyAll(); } } // 生产者 class Productor implements Runnable { private Clerk clerk; public Productor(Clerk clerk) { this.clerk = clerk; } @Override public void run() { for (int i = 0; i < 20; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { } clerk.get(); } } } // 消费者 class Consumer implements Runnable { private Clerk clerk; public Consumer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { for (int i = 0; i < 20; i++) { clerk.sale(); } } }
运行截图
这个才会得到解决。
相关文章推荐
- java多线程中的等待唤醒机制--多生产者多消费者问题
- 线程间通信、等待唤醒机制、生产者消费者问题(Lock,Condition)、停止线程和守护线程、线程优先级
- 通过生产者消费者案例理解等待唤醒机制和虚假唤醒
- day12线程间的通信,等待唤醒机制,生产者消费者问题。新锁lock(): 守护线程,interrupt()停止线程用,join()yield()
- java多线程之 生产者和消费者 线程间通信 等待与唤醒机制
- java多线程---等待/唤醒以及生产者消费者经典同步synchronized的实现
- java基础知识回顾之java Thread类学习(八)--java多线程通信等待唤醒机制经典应用(生产者消费者)
- Android(java)学习笔记71:生产者和消费者之等待唤醒机制
- 生产者消费者:等待唤醒机制(最终版代码)
- 多线程_生产者消费者之等待唤醒机制代码优化
- 多线程_生产者消费者之等待唤醒机制代码分析
- java多线程-线程间通信-示例代码-解决安全问题-等待唤醒机制wait()notify()notifyAll()
- 多线程-生产者消费者之等待唤醒机制代码优化
- 24 API-多线程(多线程(多线程JDK5Lock锁,生产者消费者等待唤醒机制,定时器),设计模式(设计原则,设计模式(简单工程,工厂方法,单例模式)
- 生产者和消费者问题【java等待通知机制实现】
- java多线程中的生产者与消费者之等待唤醒机制@Version2.0
- 多线程——等待唤醒机制经典实例:生产者消费者模式
- java多线程 生产者消费者案例-虚假唤醒
- 多线程-生产者消费者之等待唤醒机制
- 生产者和消费者之等待唤醒机制