wait(),notify(),notifyAll() 是属于Object对象的jvm级native方法,用于线程调度;
wait()
|
|
调用对象的该方法的将使线程进入等待状态,直到另外一个线程调用 notify() 或者 notifyAll() 后重新唤起该线程,该方法只能在获取到对象的内置锁之后,才能进行调用,wait() 方法调用之后,线程将放弃掉对象的内置锁,在线程再次被唤起之前,线程将重新获取到该内置锁。
可以给一个处于等待的线程可以发送 interrupt() 消息来中断等待。
A waiting thread can be sent {@code interrupt()} to cause it to prematurely stop waiting, so {@code wait} should be called in a loop to check that the condition that has been waited for has been met before continuing.
interrupt
interrupt()方法的工作仅仅是改变中断状态,并不是直接中断正在运行的线程。中断的真正原理是当线程被Object.wait(),Thread.join()或sleep()方法阻塞时,调用interrupt()方法后改变中断状态,而wait/join/sleep这些方法内部会不断地检查线程的中断状态值,当发现中断状态值改变时则抛出InterruptedException异常;对于没有阻塞的线程,调用interrupt()方法是没有任何作用。
notify()
|
|
调用对象的notify()方法,将会唤醒调用了该对象的wait()方法导致进入等待状态的线程;如果超过一个线程处于等待状态,JVM将会选择一个线程来唤醒(无序),被选择的线程不会立即执行,当前线程需要先释放掉锁之后,并且,被选择的线程还需要和其它尝试锁定该对象的线程竞争;
下面的一个对象的两个方法,比如线程 A 在调用getInt1之后线程进入休眠状态,在某一时刻notify方法被调用,线程 A 被JVM选择作为被唤醒的线程,但是,与此同时,线程 B 又调用了getInt2方法,此时线程 A 还需要和其它同样使用到了锁对象的线程(线程 B)竞争系统资源,只有线程 A 得到系统资源之后,才能继续向下执行;
|
|
notifyAll()
和notify()类似,但它是唤醒所有的处于wait状态的线程;
- 调用了wait方法使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
- 调用了notify方法通知某个正在等待这个对象的控制权的线程可以继续运行。
- 调用了notifyAll方法通知所有等待这个对象控制权的线程继续运行。
Example1
- WaitRunnable,线程开启之后,将 sleep 指定的时间,然后重置 flag,再 notifyAll() 唤醒所有的等待线程;
|
|
- main
|
|
Example2
生产者与消费者实例:
|
|
Plate是一个篮子,可以往里面放egg,同一时刻,篮子里面只能有一个或者零个egg,放入一个,必须被取走之后才能继续往里面放,取了之后,等待再放入之后再重新取;
|
|
注意 addEgg 和 getEgg 方法中,notify和wait时,锁对象是不同的!
- wait()、notify()方法都需要在获取到对象的锁(monitor)之后,才能够进行调用,否则将抛异常。获取锁通过synchronized关键字获取,锁对象与调用wait()、notify()的对象必须是同一个对象。
- synchronized方法或代码块在执行的时候获取到对象锁,调用wait()方法当前线程会进入等待状态,并且会释放掉锁,之后其它线程也可以获取该对象的锁,即其它线程可以继续调用wait()方法,notify()方法。
- notify()方法是无序的,notify()之后,其它等待状态中的线程(调用wait()方法进入等待状态)或者尝试获取锁对象的线程(synchronized关键字修饰的代码块或方法)根据CPU的调度随机获取到锁。
- notifyAll()方法用于激活所有进入等待状态的线程。