目录
一、什么是虚假唤醒?
当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功
1.比如说买货,如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了
,但是只能一个人买,所以其他人都是假唤醒,获取不到对象的锁
二、开发中虚假唤醒会造成问题的场景:
例如在多线程的场景中,判断一个线程是否需要阻塞(调用wait())时,如果用 if 判断,就会出问题,因为想想看,多有多个线程都在阻塞时,突然条件满足了(product<1),那么就会有线程唤醒阻塞着的多个线程,但是只有一个线程能拿到锁,其余没拿到锁的线程要是被唤醒了,还继续往下执行,就不对了。
if(product>=1){
wait();
}
所以解决方法是 调用wait的代码块,要用while 来代替 if :
while(product>=1){
wait();
}
三、为什么 if 会产生虚假唤醒
因为if只会执行一次,执行完会接着向下执行if()外边的 而while不会,直到条件满足才会向下执行while()外边的
四、怎么产生虚假唤醒
把 while (product >=1) {} 换成 if (product >=1) {} 就会出现虚假唤醒
下面给个 生产者消费者的代码展示:
public class Test {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Customer customer = new Customer(clerk);
new Thread(producer,"生产者A").start();
new Thread(customer,"消费者A").start();
new Thread(producer,"生产者B").start();
new Thread(customer,"消费者B").start();
}
}
class Clerk{
private int product=0;
public synchronized void add(){
while(product>=1){
System.out.println(Thread.currentThread().getName()+"---还有货物:"+product);
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
product+=1;
System.out.println(Thread.currentThread().getName()+" 补货完成,剩余货物:"+product);
notifyAll();
}
public synchronized void sale(){
while (product<=0){
System.out.println(Thread.currentThread().getName()+" 缺货中:"+product);
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 已补货,开始消费...");
product-=1;
notifyAll();
}
}
class Producer implements Runnable{
private Clerk clerk;
public Producer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i=0;i<20;i++){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.add();
}
}
}
class Customer implements Runnable{
private Clerk clerk;
public Customer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.sale();
}
}
}