先来看一个例子
一个卖面的面馆,有一个作面的厨师和一个吃面的食客,须要保证,厨师作一碗面,食客吃一碗面,不能一次性多作几碗面,更不能没有面的时候吃面;按照上述操做,进行十轮作面吃面的操做。java
用代码说话
首先咱们须要有一个资源类,里面包含面的数量,作面操做,吃面操做;
当面的数量为0时,厨师才作面,作完面,须要唤醒等待的食客,不然厨师须要等待食客吃完面才能作面;
当面的数量不为0时,食客才能吃面,吃完面须要唤醒正在等待的厨师,不然食客须要等待厨师作完面才能吃面;
而后在主类中,咱们建立一个厨师线程进行10次作面,一个食客线程进行10次吃面;
代码以下:算法
package com.duoxiancheng.code; /** * @user: code随笔 */ class Noodles{ //面的数量 private int num = 0; //作面方法 public synchronized void makeNoodles() throws InterruptedException { //若是面的数量不为0,则等待食客吃完面再作面 if(num != 0){ this.wait(); } num++; System.out.println(Thread.currentThread().getName()+"作好了一份面,当前有"+num+"份面"); //面作好后,唤醒食客来吃 this.notifyAll(); } //吃面方法 public synchronized void eatNoodles() throws InterruptedException { //若是面的数量为0,则等待厨师作完面再吃面 if(num == 0){ this.wait(); } num--; System.out.println(Thread.currentThread().getName()+"吃了一份面,当前有"+num+"份面"); //吃完则唤醒厨师来作面 this.notifyAll(); } } public class Test { public static void main(String[] args) { Noodles noodles = new Noodles(); new Thread(new Runnable(){ @Override public void run() { try { for (int i = 0; i < 10 ; i++) { noodles.makeNoodles(); } } catch (InterruptedException e) { e.printStackTrace(); } } },"厨师A").start(); new Thread(new Runnable(){ @Override public void run() { try { for (int i = 0; i < 10 ; i++) { noodles.eatNoodles(); } } catch (InterruptedException e) { e.printStackTrace(); } } },"食客甲").start(); } }
输出以下:
能够见到是交替输出的;微信
若是有两个厨师,两个食客,都进行10次循环呢?
Noodles类的代码不用动,在主类中多建立两个线程便可,主类代码以下:ide
public class Test { public static void main(String[] args) { Noodles noodles = new Noodles(); new Thread(new Runnable(){ @Override public void run() { try { for (int i = 0; i < 10 ; i++) { noodles.makeNoodles(); } } catch (InterruptedException e) { e.printStackTrace(); } } },"厨师A").start(); new Thread(new Runnable(){ @Override public void run() { try { for (int i = 0; i < 10 ; i++) { noodles.makeNoodles(); } } catch (InterruptedException e) { e.printStackTrace(); } } },"厨师B").start(); new Thread(new Runnable(){ @Override public void run() { try { for (int i = 0; i < 10 ; i++) { noodles.eatNoodles(); } } catch (InterruptedException e) { e.printStackTrace(); } } },"食客甲").start(); new Thread(new Runnable(){ @Override public void run() { try { for (int i = 0; i < 10 ; i++) { noodles.eatNoodles(); } } catch (InterruptedException e) { e.printStackTrace(); } } },"食客乙").start(); } }
此时输出以下:
this
虚假唤醒
上面的问题就是"虚假唤醒"。
当咱们只有一个厨师一个食客时,只能是厨师作面或者食客吃面,并无其余状况;
可是当有两个厨师,两个食客时,就会出现下面的问题:线程
- 初始状态
- 厨师A获得操做权,发现面的数量为0,能够作面,面的份数+1,而后唤醒全部线程;
- 厨师B获得操做权,发现面的数量为1,不能够作面,执行wait操做;
- 厨师A获得操做权,发现面的数量为1,不能够作面,执行wait操做;
- 食客甲获得操做权,发现面的数量为1,能够吃面,吃完面后面的数量-1,并唤醒全部线程;
6. 此时厨师A获得操做权了,由于是从刚才阻塞的地方继续运行,就不用再判断面的数量是否为0了,因此直接面的数量+1,并唤醒其余线程;3d
7. 此时厨师B获得操做权了,由于是从刚才阻塞的地方继续运行,就不用再判断面的数量是否为0了,因此直接面的数量+1,并唤醒其余线程;
这即是虚假唤醒,还有其余的状况,读者能够尝试画画图分析分析。code
解决方法
出现虚假唤醒的缘由是从阻塞态到就绪态再到运行态没有进行判断,咱们只须要让其每次获得操做权时都进行判断就能够了;
因此将blog
if(num != 0){ this.wait(); }
改成图片
while(num != 0){ this.wait(); }
将
if(num == 0){ this.wait(); }
改成
while(num == 0){ this.wait(); }
便可。
微信搜索:code随笔 欢迎关注乐于输出Java,算法等干货的技术公众号。