Object.wait()会让当前线程等待,并释放锁。进而notifyAll时,会唤醒其他线程争抢资源;但是wait有个特点,在哪里睡,唤醒时就在那里醒来;所以官方推荐wait应写在循环里,避免出现虚假唤醒问题。
因为我们再线程间通信时,希望某些线程没有拿到资源时不去执行逻辑,当拿到资源时再去执行逻辑。如果写到if里面,当没有拿到资源,进入if而等待。当他再次拿到资源时,不会去进行if判断进而直接执行逻辑。所以应该写到循环里面。
例子:多个线程进行通信,同时操作一个变量,使之一直加1减1,保持在0和1之间。
public class ThreadNotify01 {
public static void main(String[] args) {
Share share = new Share();
//第三步:创建多个线程,调用资源类的操作方法
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "aa").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "bb").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "cc").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "dd").start();
}
}
//第一步:创建资源类,定义属性和操作方法
class Share{
//初始值
private int number = 0;
//+1方法
public synchronized void incr() throws InterruptedException {
//第二部 判断、干活、通知
if (number != 0) {
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "::" + number);
//通知其它线程
this.notifyAll();
}
//-1方法
public synchronized void decr() throws InterruptedException {
//第二部 判断、干活、通知
if (number != 1) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "::" + number);
//通知其它线程
this.notifyAll();
}
}
执行结果:因为进入if后等待,再次唤醒不会去进行再次判断,导致虚假唤醒问题:
将if改为while,问题解决:
public class ThreadNotify01 {
public static void main(String[] args) {
Share share = new Share();
//第三步:创建多个线程,调用资源类的操作方法
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "aa").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "bb").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "cc").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "dd").start();
}
}
//第一步:创建资源类,定义属性和操作方法
class Share{
//初始值
private int number = 0;
//+1方法
public synchronized void incr() throws InterruptedException {
//第二部 判断、干活、通知
while (number != 0) {
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "::" + number);
//通知其它线程
this.notifyAll();
}
//-1方法
public synchronized void decr() throws InterruptedException {
//第二部 判断、干活、通知
while (number != 1) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "::" + number);
//通知其它线程
this.notifyAll();
}
}