今天正好碰到这个问题,也疑惑了很久。看了一圈知乎上的答案,感受没说到根上。因此本身又好好Google了一下,终于找到了让本身信服的解释。html
先说两个概念:锁池和等待池java
- 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),因为这些线程在进入对象的synchronized方法以前必须先得到该对象的锁的拥有权,可是该对象的锁目前正被线程A拥有,因此这些线程就进入了该对象的锁池中。
- 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中
- Reference:java中的锁池和等待池
而后再来讲notify和notifyAll的区别.net
- 若是线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
- 当有线程调用了对象的 notifyAll()方法(唤醒全部 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的全部线程移动到锁池中,等待锁竞争。
- 优先级高的线程竞争到对象锁的几率大,倘若某线程没有竞争到该对象锁,它还会留在锁池中,惟有线程再次调用 wait()方法,它才会从新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
- Reference:线程间协做:wait、notify、notifyAll
综上,所谓唤醒线程,另外一种解释能够说是将线程由等待池移动到锁池,notifyAll调用后,会将所有线程由等待池移到锁池,而后参与锁的竞争,竞争成功则继续执行,若是不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会唤醒一个线程。线程
有了这些理论基础,后面的notify可能会致使死锁,而notifyAll则不会的例子也就好解释了htm
顺便说下:sleep和wait的区别:对象
对于sleep()方法,该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。blog
sleep()方法致使了程序暂停执行指定的时间,让出cpu该其余线程,可是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态(能够理解为抱着锁睡觉...)。get
在调用sleep()方法的过程当中,线程不会释放对象锁。it
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备(扔掉锁去睡觉...)。io