首先wait和notify都是属于Object类中的方法,所以在使用的时候new一个类对象都是可以使用的(不能是基本数据类型)。
多个线程之间的调度是无序的,随机的,但是有些场景下我们还是需要线程之间的执行顺序是有序的,之间总结过join方法的使用,join方法是一个办法,可以让线程和线程之间是完整的串行执行的,但是功能有限。
比如现在有t1和t2两个线程,t1的代码逻辑执行到一半的时候,此时我需要让t1线程停下来去执行t2线程,此时join方法是做不到的。但是wait是可以做到的。
在t1线程中调用wait方法,就会让这个线程进行阻塞状态,暂时不参与CPU的调度执行,此时t2线程就可以执行了,那t2线程的逻辑执行完了,需要让t1这个线程再继续执行,就需要通知t1线程,用notify方法,告诉t1线程,此时你可以继续执行了。notify就是把这个线程唤醒。
所以wait:当发现条件不满时、时机不成熟时,先进行阻塞等待状态
notify:其他的线程有这个成熟的条件了,再通知wait的线程继续执行。
此时就更可以灵活的控制线程的顺序,让线程之间有序的执行。
使用wait方法要注意:此时在某一个线程中调用wait方法时,wait干了3件事:
1.释放锁 2.进入阻塞状态等待 3.等收到notify通知的时候,就唤醒,然后尝试重新获取锁
所以在使用wait方法时要和synchronized(监视器锁)搭配使用,也就是在wait的逻辑要写在synchronized中,(先要获取一个锁才能进行释放锁操作)。而且wait和notify方法的执行也是有顺序的,wait方法要先执行完成能之后才能执行notify方法,也就是说notify方法也要写在锁代码块中,并且和wait方法同时竞争一个锁对象,等wait释放锁之后,才可以通知。如果没有wait,就notify,此时也不会有副作用,但是我们想要的功能就实现不了了。
可以参考下面代码:
/*wait主要做三件事:1.解锁 2.阻塞等待 3.当收到通知时就唤醒同时尝试重新获取锁
* 所以wait必须要写到synchronized代码块里面(因为只有先加锁才能解锁)*/
public class ThreadDemo18 {
public static void main(String[] args) throws InterruptedException {
System.out.println("notify之前:");
Object locker = new Object();
Thread t1 = new Thread(() -> {
try {
System.out.println("wait开始 ");
synchronized (locker) {
locker.wait();
}
System.out.println("wait结束 ");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
Thread.sleep(1000);
Thread t2 = new Thread(() -> {
synchronized (locker) {
locker.notify();
}
});
t2.start();
System.out.println("notify之后 ");
}
}
此时要区分wait notify和join的区别:
join方法是让两个线程完整的执行串行,有t1和t2两个线程,要等t1线程完全执行完了才让t2线程继续执行,而wait notify方法是让t1线程执行一部分,然后让t2线程开始执行,然后再让t1线程继续执行,再让t2线程执行.........
还有一个唤醒操作,是notifyAll方法,这个方法是和notify方法并列的存在的,比如此时有一个场景是有多个线程都等待一个线程,t1,t2,t3线程都调用wait方法,(并且都进入waiting状态)如果此时在main线程中去调用notify方法,就会随机唤醒一个123其中的一个线程。(另外两个线程仍然是waiting状态(调用wait方法会出现 waiting状态))
notifyAll方法则会同时唤醒三个线程,让这3个线程同时去获取一个锁对象,然后一次执行