我们知道在synchronized中使用wait和notify会产生虚假唤醒的问题,使用while代替if就可以解决虚假唤醒
具体原因是我们学习wait和notify的时候没有理解清楚,注意:
notify之后被唤醒的那个线程会接着执行而不是重新执行
用一个例子来证明:
public class WaitNotifyTest {
public static void main(String[] args) {
Wait wait=new Wait();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
wait.increament();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
wait.decreament();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
}
}
class Wait{
private Integer num=0;
public synchronized void increament() throws InterruptedException {
System.out.println("A被唤醒了");
while (num!=0){
System.out.println("重新进入A,然后A执行wait,A被阻塞了,其他线程(包括自己)获得运行机会");
this.wait();
System.out.println("A继续执行");
}
num++;
System.out.println(Thread.currentThread().getName()+"->"+num);
this.notifyAll();
System.out.println("A唤醒其他的线程");
}
public synchronized void decreament() throws InterruptedException {
System.out.println("B被唤醒了");
while (num==0){
System.out.println("重新进入B,然后B执行wait,B被阻塞了,其他线程(包括自己)获得运行机会");
this.wait();
System.out.println("B继续执行");
}
num--;
System.out.println(Thread.currentThread().getName()+"->"+num);
this.notifyAll();
System.out.println("B唤醒其他的线程");
}
}
再看执行结果:
假设notify唤醒的那个线程是重新执行的,那么每次都会打印"A被唤醒了",但是图片中标红的第二段直接打印"A继续执行",说明是继续执行而不是重新执行
理解了notify是继续执行而不是重新执行,那么虚假唤醒就好理解了,如果是用if的话,代码继续执行,那么这个线程获得锁之后就不会再检测条件了(因为if只执行一次,它是不会再次检测num是否等于0的),而是继续执行if代码块下面的语句.当我们使用while的时候,条件语句num!=0会被循环判断,条件为真就会一直循环,条件为假才会继续执行while语句块下面的.
理解的虚假唤醒最重要的一点就是理解notify唤醒的线程会继续执行而不是重新执行