MelonBlog

java线程状态Waiting和Blocked的区别

image

我们知道JVM线程的状态分为:NewRunnableRunningWaitingBlockedTerminated


New、Runnable、Running、Terminated这几个状态都非常好理解,各自的含义都很清晰:

New:新建的,但是还未启动
Runnable:可执行的,但是还在等待CPU时钟
Running:已分配CPU时钟,执行中
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()方法。

它们的作用分别为:

wait():当一个线程执行一个对象的wait()方法时,这个线程会停止执行当前的任务,进入Waiting状态,直到被其他线程通过notify()方法唤醒
notify():当执行某一个对象的notify()方法时,会”唤醒“一个因为调用该对象而进入Waiting状态下的线程


这里说的唤醒,并不是直接让线程从Waiting进入Running状态,一个线程要到达Running状态之前,必须处于Runnable状态,并且OS的调度器为该线程分配了CPU时钟之后,这个线程才能变成Running状态,并且开始执行指令。notify()并不会让Waiting线程直接变成Runnable,而是Blocked。

image

为什么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队列。

image

在理解了一个线程的Waiting状态和Blocked状态的区别之后,我们可以延伸一个有意思问题:sleep()和wait()的区别

在理解sleep和wait的区别之后,我相信大家对线程的理解会更进一步。