1.前言
在Java多线程环境中,线程之间是抢占式执行的,线程的调度是随机的.这就很难受了. 在很多情况下我们希望线程以我们想要的顺序来执行. 这就需要wait和notify这两个方法
2.wait和notify的基本使用
首先是wait方法
wait是Object类的方法,而Java中的类都是间接或直接继承于Object类. 因此只要是类的实例都可以调用wait方法
运行上述代码:
可以看到这里抛出了一个 非法的锁状态异常
其实wait方法的执行分为三步:
1.释放当前锁
2.进行等待通知
3.满足一定的条件(其它线程调用notify),被唤醒,然后重新获取锁
什么要先释放锁,再进行等待通知呢?
因为wait如果不释放锁,可能会影响到其它线程的执行,就是为了保证不影响其它线程的执行
因此wait与锁密不可分
对上述代码进行加锁,就不会抛出异常了.
但上述代码的执行并没有结束,因为线程在调用wait方法之后,会一直处于阻塞状态,直到有其它线程调用notify方法为止.
而调用notify方法的线程要和调用wait方法的线程是针对同一个对象进行加锁的才行.
示例:
public static void main(String[] args) { Object object = new Object(); Thread t1 = new Thread(() ->{ while(true){ synchronized (object){ System.out.println("wait 之前"); try { object.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("wait 之后"); } } }); t1.start(); Thread t2 = new Thread(() ->{ while(true){ synchronized (object){ System.out.println("notify 之前"); object.notify(); System.out.println("notify 之后"); } try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); t2.start(); }
运行截图:
上述标注的是一组 wait和notify方法的执行流程.
注: 如果先调用的是notify方法,代码并不会有什么影响.
3. notifyAll方法
Java中除了使用notify方法唤醒线程,还有一个notifyAll方法.
当一个线程调用了某个对象的notifyAll方法后,该对象上的所有等待线程将被唤醒,即使唤醒了所有在等待的线程,这些线程之间也要进行锁竞争(串行执行)。
4. wait和sleep方法的对比
wait方法和sleep方法的对比也是面试中经常会问到的问题
对于这个问题,可以从相同点和不同点进行回答:
相同点: 都是让线程进入阻塞等待状态
不同点:sleep方法是通过时间来通知唤醒,而wait方法则需要使用notify或notifyAll进行唤醒
5. 总结
wait和notify方法用于实现线程间的协作和通信.
wait方法使线程进入等待状态,notify方法唤醒一个等待线程,notifyAll方法唤醒所有等待线程。
通过上述三个方法,可以使线程按照特定的顺序执行或者等待某个条件满足后再执行。