java线程状态Waiting和Blocked的区别
我们知道JVM线程的状态分为:New、Runnable、Running、Waiting、Blocked、Terminated。
New、Runnable、Running、Terminated这几个状态都非常好理解,各自的含义都很清晰:
但是Waiting和Blocked状态就比较迷惑,我们都知道线程在某些场景下需要被挂起让出CPU资源,那被挂起的线程到底是Waiting还是BLocked呢,下面就聊聊这两者的区别。
wait()和notify()
要理解线程什么时候进入Waiting状态,首先要知道wait()和notify()方法。
除了wait()和notify(),Object类还有好几个wait()的重载方法和一个notifyAll()方法,但是原理都类似,这里只讨论wait()和notify()导致的线程状态变更。
wait()和notify()方法是Object类里面的2个方法,因为java里Object是所有类的基类,所以所有我们创建的类都能够调用wait()和notify()方法。
它们的作用分别为:
这里说的唤醒,并不是直接让线程从Waiting进入Running状态,一个线程要到达Running状态之前,必须处于Runnable状态,并且OS的调度器为该线程分配了CPU时钟之后,这个线程才能变成Running状态,并且开始执行指令。notify()并不会让Waiting线程直接变成Runnable,而是Blocked。
为什么notify()方法不是把线程变成Runnable而是Blocked呢,因为线程还需要拿到一个Monitor Lock才能被激活。
Monitor Lock
我们知道wait()和notify()必须要在synchronized块里面使用,如果不这样用,jvm会抛一个IllegalMonitorStateException 异常,所以wait()、notify()、synchronized是捆绑使用的。
一个线程有没有权限执行synchronized块,实际上就是看这个线程是否持有Monitor Lock,我们结合一下上文讲的,notify()会”唤醒“一个Waiting线程,所以我们可以知道,这个被唤醒的线程下一步还需要获取Monitor Lock之后,才有权限执行synchronized块里面的代码,而notify()方法执行完了之后,并不代表当前线程退出synchronized代码块了,可能还有其他的代码需要执行。
所以在获取到Monitor Lock之前,这个线程就是Blocked状态。当一个Running状态下的线程执行完synchronized代码块之后,jvm才会随机让某一个Blocked状态下的线程持有Monitor Lock,这个线程只用等待OS调度器分配CPU时钟,然后就可以执行代码逻辑了。
总结
在了解了wai/notify 和 monitor lock之后,我们可以理解,Waiting状态的线程和BLocked状态的线程实际上是处于两个不同的队列里,notify()实际上就是将waiting队列的线程转移到blocked队列。
在理解了一个线程的Waiting状态和Blocked状态的区别之后,我们可以延伸一个有意思问题:sleep()和wait()的区别
在理解sleep和wait的区别之后,我相信大家对线程的理解会更进一步。