Java并发 | 21.[方法] wait( )、wait(long m)、notify( )、notifyAll( )

1. API概述

以下四个方法都是操作「当前对象obj对应的Monitor对象(之后简称当前对象锁)」中的 Owner 或 WaitSet。

这些方法执行的前提都是当前线程必须持有锁。

方法签名功能概述备注
void Object.wait()使当前对象锁中Owner进入WAITING状态当前线程必须要持有锁
void Object.wait(long m)使当前对象锁中Owner进入TIMED_WAITING状态当前线程必须要持有锁,m毫秒后会自动唤醒
void Object.notify( )唤醒当前对象锁中WaiSet的随机一个线程当前线程必须要持有锁
void Object.notifyAll( )唤醒当前对象锁中WaiSet的所有线程当前线程必须要持有锁

2. obj.wait( ) / obj.wait( long m ) 底层原理:

  • [释放锁并进入WaitSet]调用obj.wait( )/obj.wait( long m )后,当前对象锁的 Owner 将进入 WaitSet 并进入「WAITING/TIMED_WAITING」状态,此时 Owner 空缺,因此释放了锁
  • [唤醒EntryList的阻塞线程]由于 Owner 空缺,根据JVM底层算法,某个EntryList中的线程将被唤醒,并成为新的Owner。

wait(-)底层机制

3. 唤醒 WAITING / TIMED_WAITING 线程的三种方式

处于WaitSet的线程会在三种情况下会被唤醒,进入到 EntryList 并进入 BLOCKED 状态:

  • [自动唤醒]若线程因调用了obj.wait( long m )而进入了TIME_WAITING状态,会在m毫秒后被自动唤醒;
  • [随机唤醒]在调用obj.notify( )后,JVM会将WaitSet中 随机一个线程唤醒;
  • [全部唤醒]在调用obj.notifyAll( )后,JVM会将WaitSet的所有线程唤醒。

三种唤醒方式

4. Thread.sleep( long m ) VS obj.wait( long m )

4.1. 相同点

  • [状态一致、自动唤醒]sleep( long m )wait( long m )调用后,二者的状态都是TIMED_WAITING,m毫秒后将被自动唤醒;
  • [被打断后均会抛出异常]处于TIMED_WAITING(和WAITING)状态的线程被打断后都会抛出异常,并将标识重置为false。

4.2. 不同点

  • [静态vs非静态]Thread.sleep( long m )是一个静态方法,而obj.wait( long m)是Object类的成员方法,这意味着这个obj对应的 Monitor 对象必须要有 Owner;
  • [是否释放锁]sleep( long m )调用后仍为Owner(不释放锁),wait( long m )调用后则是让自己从 Owner 进入到 WaitSet(释放了锁);
  • [wait有无参方法]无参方法wait( )调用后将进入WAITING状态,只能被打断或被nodify( ) / nodifyAll( )唤醒;

参考资料

[视频] 04.039-小故事-wait-notify

[视频] 04.040-wait-notify-工作原理

[视频] 04.041-wait-notify-api1

[视频] 04.042-wait-notify-api2

[视频] 04.043-wait vs sleep


版权声明:本文为xyxyxyxyxyxyx原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。