属于Object类的两个方法。
wait(),使当前线程等待,直到另一个线程使用notify()或notifyAll()方法唤醒。
notify(),唤醒正在wait等待的线程。
官方API文档说明:
既然说明了wait()和notify()的功能,那么我们手撸一段代码来试下:
/**
* @author Shamee loop
* @date 2023/4/9
*/
public class ObjectWaitNotifyDemo {
public static void main(String[] args) {
NumberOper object = new NumberOper();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
object.add();
}
}, "thread-add-1").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
object.sub();
}
}, "thread-sub-1").start();
}
}
/**
* 定义一个数量操作类
* 其中包含两个方法,
* 一个是对临界值number++;当判断number!=0,就等待number变成0后在执行。同时唤醒另一个线程
* 另一个方法是对临界值number--;当判断number==0,就等待number!=0后在执行。同时唤醒另一个线程
* 同时两个方法分别加了synchronized锁,确保线程安全问题
*/
class NumberOper {
private int number = 0;
public synchronized void add() {
if(number != 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println("线程" + Thread.currentThread().getName() + "执行了add(),number====>" + number);
this.notifyAll();
}
public synchronized void sub() {
if(number == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println("线程" + Thread.currentThread().getName() + "执行了sub(),number====>" + number);
this.notifyAll();
}
}
预期的结果应该是:
线程tread-add-1执行,判断到number==0,就number++;这时候控制台打印number==>1
线程tread-sub-1执行,判断到number==0,则进行等待;等到tread-add-1执行完毕后,唤醒了tread-sub-1执行,这时候tread-sub-1就执行了number--操作;这时候控制台打印number==>0
实际执行结果:
果然是这样。那么这篇文章就结束了吗?
上面操作示例,只开启了2个线程。但如果开启多个线程呢?我们再试下:
/**
* @author Shamee loop
* @date 2023/4/9
*/
public class ObjectWaitNotifyDemo {
public static void main(String[] args) {
NumberOper object = new NumberOper();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
object.add();
}
}, "thread-add-1").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
object.sub();
}
}, "thread-sub-1").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
object.add();
}
}, "thread-add-2").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
object.sub();
}
}, "thread-sub-2").start();
}
}
/**
* 定义一个数量操作类
* 其中包含两个方法,
* 一个是对临界值number++;当判断number!=0,就等待number变成0后在执行。同时唤醒另一个线程
* 另一个方法是对临界值number--;当判断number==0,就等待number!=0后在执行。同时唤醒另一个线程
* 同时两个方法分别加了synchronized锁,确保线程安全问题
*/
class NumberOper {
private int number = 0;
public synchronized void add() {
if(number != 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println("线程" + Thread.currentThread().getName() + "执行了add(),number====>" + number);
this.notifyAll();
}
public synchronized void sub() {
if(number == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println("线程" + Thread.currentThread().getName() + "执行了sub(),number====>" + number);
this.notifyAll();
}
}
预期的结果应该是:
线程tread-add-1执行,判断到number==0,就number++;这时候控制台打印number==>1
线程tread-sub-1执行,判断到number==0,则进行等待;等到tread-add-1执行完毕后,唤醒了tread-sub-1执行,这时候tread-sub-1就执行了number--操作;这时候控制台打印number==>0
实际执行结果:
很明显结果不对了,甚至出现了负数等值。这是为啥?
我们来看下官方API文档怎么说:
而文档给出来的建议是,临界值的判断使用while。我们再试下:
public synchronized void add() {
while(number != 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println("线程" + Thread.currentThread().getName() + "执行了add(),number====>" + number);
this.notifyAll();
}
public synchronized void sub() {
while(number == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println("线程" + Thread.currentThread().getName() + "执行了sub(),number====>" + number);
this.notifyAll();
}
执行结果:
似乎一切正常了。那么是为什么呢?用if不行用while就可以。
什么是虚假唤醒:是不想唤醒它或者说不确定是否应该唤醒,但是被唤醒了。对程序来说,wait 方法应该卡住当前程序,不应该往后执行;但是实际上并没有被卡住,而是在非预期的时间程序正常执行了,没有程序没有被卡住就是被虚假唤醒了
在if条件判断下,他只会判断一次,如果这时候被notify唤醒之后,程序会从当前等待的代码继续执行(也就是进入了if判断之后的代码)。而如果使用while判断,唤醒后会重新执行while循环条件。如果条件成立,就继续wait。因此就出现了上面看到的现象。
好了,又白嫖了一个无聊的小知识,没用但有趣的小知识。