1. wait,notify,notifyAll
1.1wait原理
1.2用法
-
阻塞阶段
让线程暂时休息,等到时机成熟再唤醒继续运行。
在对象执行wait方法的时候,线程必须拥有这个对象的monitor锁,然后释放锁,进入阻塞状态。 -
唤醒阶段
- 另一个线程调用这个对象的notify方法唤醒线程(选取一个)且刚好被唤醒的是这个线程;
- 另一个线程调用这个对象的notifyAll方法;
- 过了wait(long timeout)规定的超时时间,传入0就是永久等待;
- 线程自身调用interrupt()
-
遇到中断
抛出异常,释放掉monitor。
1.3notify唤醒
- notify方法只应该被拥有过该对象的monitor的线程调用
- 一旦线程被唤醒,线程便会从“等待线程集合”中被移除。
- 要等刚才执行notify的线程退出被synchronized修饰的代码并释放monitor后才能抢占monitor,抢到之后才能继续执行。
1.4 wait/notify/notifyAll的特点、性质
- 必须先拥有monitor
- notify只能唤醒其中一个
- 属于Object类
- 只会释放持有的调用wait的对象的锁。
1.5 常见面试题
- 手写两个线程交替打印0-100的奇偶数:synchronized或者wait/notify实现。
- 手写生产者消费者模式。
- 为什么wait需要在同步代码块内使用
没有同步:如果线程1在wait之前切换到另一个线程,另一个线程的notify和notifyAll都执行完了,再切回来执行wait,这样就会造成wait无法唤醒,造成永久等待 。
2. sleep
-
作用:让线程只在预期的时间执行,其他时候不占用CPU资源。
-
不释放锁:
- 包括synchronized和lock
-
中断会抛出InterruptedException并清除中断状态。
-
TimeUnit.SECONDS.sleep()更加优雅。会忽略负参,而不是报错;有更多的API可以调用,例如SECONDS而不是只有ms。
-
面试题:
- wait/notify、sleep异同(方法属于那个对象?线程状态怎么切换)
相同
- 阻塞
- 响应中断
不同
- wait/notify在同步方法中使用
- sleep不释放锁
- sleep要指定时间
- 所属类不同
- wait/notify、sleep异同(方法属于那个对象?线程状态怎么切换)
join
作用:因为新的线程加入,所以要等它执行完再执行(主等子)。
用法:main等待thread1执行完毕。(底层调用的是wait方法)
三个线程abc,怎么保证b在a后运行,c在b后运行(join)
-
中断
实际上是主线程阻塞时被中断。 -
在join期间,主线程是什么状态:Waiting
yield
作用:释放线程的时间片,还是Runnable状态。
定位:JVM不保证遵循
yield和sleep区别:是否随时可能再次被调度