今天遇到了并发编程中的虚假唤醒现象, 首先上一段典型的生产者消费者代码:
package juc;
class AirConditioner {
private int number = 0;
public synchronized void increment() throws InterruptedException {
// 出现虚假唤醒
if (number != 0) {
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
// 出现虚假唤醒
if (number == 0) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "\t" + number);
this.notifyAll();
}
}
/**
* 实现一个线程变量+1, 一个线程变量-1
* 来10轮
*/
public class ThreadWaitNotifyDemo {
public static void main(String[] args) {
AirConditioner airConditioner = new AirConditioner();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
airConditioner.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(300);
airConditioner.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(400);
airConditioner.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(500);
airConditioner.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
虚假唤醒的表现
虚假唤醒的表现为: 有时候资源会出现"超卖"现象. 也就是出现负数, 而这是不应该出现的.
关键点就是判断合法性的时候, 使用了if进行判断.
过程
使用if进行判断后, 消费者线程A进入wait()阻塞态, 释放锁, 紧接着有另一个消费者线程B进入, 也进入阻塞态.
紧接着生产者线程C生产, 唤醒两个消费者线程AB, 此时资源数量为1.
如果使用了if, 两个消费者被唤醒后, 线程将继续执行下方的代码块, 导致结果变成-1.
为什么叫虚假唤醒?
站在两个消费者线程的角度上讲, 无论哪一个线程抢到了资源, 另一个线程的唤醒就可以被认为是没有必要的, 也就是被虚假唤醒了.