wait()/notify()原理
- 当前线程必须拥有此对象的monitor监视器(锁)。(不获取锁直接用Object对象调用wait和notify,会报错java.lang.IllegalMonitorStateException)
- 当前线程调用wait() 方法,线程就会释放此锁的所有权,并等待。
- 直到另一个线程通过notify()方法或notifyAll()方法通知在该对象的监视器(锁)上等待的线程唤醒。
- 然后线程等待,直到它可以重新获得该对象的监视器(锁)的所有权,然后继续执行(被唤醒之后还需要等待直到获取锁才能继续执行)
wait和notify是object中的方法,就是等待和唤醒:
- 一个线程持有锁但是需要等待一些资源,那么就先进入等待队列。
- 其他线程可以先持有锁并执行同步代码块中的内容
- 等到带着资源的另一个线程抢占到锁,并唤醒等待线程,等待线程就重新进入到阻塞队列,如果当前无其他线程竞争,就可以直接抢占锁并执行。
原理分析
- Owner线程发现条件不满足,调用wait()方法,即可进入WaitSet变为WAITING状态。
- BLOCKED和WAITING的线程都处于阻塞状态,不占用CPU时间片。
- BLOCKED线程会在Owner线程释放锁时唤醒。
- WAITING线程会在Owner线程调用 notify() 或notifyAll()时唤醒,但唤醒后并不以为这立刻获得锁,人需进入EntryList重新竞争。
对上图进行举例说明:
- Thread-0先抢占了锁,Owner记录当前抢占锁的线程,因为Thread-0缺少了部分资源调用wait进行等待,进入等待队列。
- Thread-1同理.
- 当前阻塞队列EntryList中有Thread-3、Thread-4和Thread-5,持有锁的线程是Thread-2
- Thread-2中调用了notify就会随机唤醒一个线程,如果唤醒的线程并没有得到需要的资源,那么就会虚假唤醒。
- 因此多个线程等待,我们使用notifyAll,然后搭配while循环判断条件就是Thread-2带来的资源是否是等待线程需要的资源,不是就继续等待,是就进入到EntryList重新排队。
wait和sleep的区别和共同点
- sleep方法属于Thread类, wait属于Object方法
- sleep不用强制与synchronized使用,但wait和synchronized需要一起使用
- sleep在睡眠时不会释放占用对象的锁,wait会释放,性能更高
- 共同点:他们都有状态TIMED-WAITING