False wakeup of wait and notify in Java

preface

This blog is from https://www.cnblogs.com/clover-forever/p/12616869.html

Write it down here for future review.

The concept of false Awakening

JDK official document explanation:

Therefore, when using wait and notify together, if you use if as the condition, false wake-up will occur, so you must use while as the loop condition. Here are some examples:

First, create a resource class: (in multithreading, the resource class and thread operation are generally decoupled and not placed in the same class. The object of the resource class will be created only when the thread operates the resource class.)

package com.test;

/**
* 资源类
* @author Huxudong
* @createTime 2020-04-01 21:57:39
**/
public class Resource {
/** 产品数 */
private int product = 0;

/** 进货 */
public synchronized void get() {
if(product >= 10) {
System.out.println(Thread.currentThread().getName()+":"+"产品已满!");
/** 当商品已经满的时候,进货线程挂起 */
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

/** 进货 */
System.out.println(Thread.currentThread().getName()+":"+ ++product);
/** 唤醒其他线程 */
this.notifyAll();

}

/** 售货 */
public synchronized void sale() {
if(product <= 0) {
System.out.println(Thread.currentThread().getName()+":"+"产品已空");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

/** 售货 */
System.out.println(Thread.currentThread().getName()+":"+ --product);
/** 唤醒其他线程 */
this.notify();
}
}

然后再创建线程来操作我们的资源类(通过java8新特性Lambda表达式直接创建)

package com.test;

import java.util.concurrent.TimeUnit;

/**
* 线程操作资源类,实现线程与资源类的解耦合
* @author Huxudong
* @createTime 2020-04-01 23:13:54
**/
public class TestPc {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
/** 睡眠,便于观察结果 */
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.get();
}
},"生产者A").start();



new Thread(()->{
for (int i = 0; i < 20; i++) {
resource.sale();
}

},"消费者C").start();

new Thread(()->{
for (int i = 0; i < 20; i++) {

resource.get();
}

},"生产者B").start();

new Thread(()->{
for (int i = 0; i < 20; i++) {
resource.sale();
}

},"消费者D").start();
}
}

先来看看如果使用if条件会发生什么:

Yes, you are right. How can there be negative numbers? It must be wrong. Calm down and analyze it. I still have a clue. I know where the problem is (then you are a person who is not surprised, very powerful).

Let's analyze. At the beginning, we called the threads of consumer C and D (because we wrote that sleep is in the producer). At this time, the consumer found that the product resource is 0. Therefore, the two brothers of consumer C and D can't but call the wait method. They sleep and release the lock.

But at this time, the first consumer has awakened, the engine begins to produce products, and after production, all waiting consumer threads are awakened. The two brothers C and d finally woke up. Brother D got the lock first, so he consumed a product first, and then found that there was no product, and he went to sleep sadly. But don't forget, at this time, another brother C was awakened. You awakened others, and they always did something. Otherwise, it would be hard. Unfortunately, At this time, the judgment condition is if, so at this time, brother C is not constrained by the condition. Then he executes his own sleep code above and resolutely consumes another product. It turns out that after brother D consumes, it is already 0. If brother C consumes minus one, it will be - 1, and so on. It is found that if the judgment conditions are not used well, the wake-up C brother is equivalent to false wake-up, which will bring unpredictable errors to the program. Therefore, it is necessary to use while to judge here. Let's take a look at the result of replacing if with while.

This time, the result is quite normal. Why use while? As mentioned above, even if all consumer threads are awakened, the while loop judgment will be kept at this time. If the condition is 0 at this time, brother C can't issue while, and he won't return the subtraction operation of the following consumer products, so this error will be avoided. This is also officially advocated. When using wait and notifyAll, you must use the while loop condition to judge.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>