首先来看一组代码
代码来源于java多线程基础总结【四】虚假唤醒
package com.zsjt.test;
public class Test1 {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(producer, "生产者1").start();
new Thread(consumer, "消费者1").start();
}
}
class Clerk {//店员类
private static final int TOTAL = 1;//数字取1是为了放大问题
private int num = 0;
public synchronized void get() {//店员买货
if (num >= TOTAL) {
System.out.println(Thread.currentThread().getName() + " : "+"库存已满,无法进货");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " : " + (++num));
this.notifyAll();
}
public synchronized void sale() {//店员卖货
if (num <= 0) {
System.out.println(Thread.currentThread().getName() + " : "+"库存已空,无法卖货");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " : " + (num--));
this.notifyAll();
}
}
class Producer implements Runnable {
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);//放大问题
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
clerk.sale();
}
}
}
本例中,生产者负责进货,消费者负责卖货,运行结果如下所示
可以看到,消费者在num==0时,提示无库存,并进入等待,待生产者进货后唤醒消费者,消费者再进行卖货,这是没有问题的,但是当我们再加入一组生产者和消费者呢,我们直接在main方法中加入,代码如下
package com.zsjt.test;
public class Test1 {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(producer, "生产者1").start();
new Thread(consumer, "消费者1").start();
new Thread(producer,"生产者2").start();
new Thread(consumer,"消费者2").start();
}
}
下面是有多个生产者或者消费者运行结果
可以看到 数据中出现负数,此时就出现问题,这就是虚假唤醒,那么问题出现的原因是什么呢?
我们来看一下代码逻辑,首先消费者执行,此时num==0 消费者打印出库存不足,两个消费者都进入阻塞状态,然后生产者进货,当生产者线程进货后,此时将两个消费者唤醒,消费者会直接执行wait()方法后的代码,即num--,因为两个消费者都执行了此代码,则将num变为负数,这就是出现错误的原因。jdk 帮助文档提供的解决方式就是将wait 方法放在while循环中,将if替换为while,这样就会在线程被唤醒后,会继续执行判断该线程是否等待的逻辑,即能保证数据安全,使用if的情况下两个消费者线程都会直接执行wait后的代码
下面是替换后的代码
package com.zsjt.test;
public class Test1 {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(producer, "生产者1").start();
new Thread(consumer, "消费者1").start();
new Thread(producer,"生产者2").start();
new Thread(consumer,"消费者2").start();
}
}
class Clerk {//店员类
private static final int TOTAL = 1;//数字取1是为了放大问题
private int num = 0;
public synchronized void get() {//店员买货
while (num >= TOTAL) {
System.out.println(Thread.currentThread().getName() + " : "+"库存已满,无法进货");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " : " + (++num));
this.notifyAll();
}
public synchronized void sale() {//店员卖货
while (num <= 0) {
System.out.println(Thread.currentThread().getName() + " : "+"库存已空,无法卖货");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " : " + (num--));
this.notifyAll();
}
}
class Producer implements Runnable {
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);//放大问题
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
clerk.sale();
}
}
}
此时数据正常
原文:https://www.cnblogs.com/zhaoshiqimr/p/14846392.html