前言
本文隶属于专栏《100个问题搞定Java并发》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构和参考文献请见
100个问题搞定Java并发
正文
如果你阅读 JDK 有关 Thread 类的 API 文档,可能还会发现两个看起来非常有用的接口,即线程挂起( suspend )和继续执行( resume )。
这两个操作是一对相反的操作,被挂起的线程,必须要等到 resume 方法操作后,才能继续指定。
乍看之下,这对操作就像 Thread.stop() 方法一样”好用”。
但如果你仔细阅读文档说明,会发现它们也早已被标注为废弃方法,并不推荐使用。
不推荐使用 suspend 方法去挂起线程是因为 suspend 方法
在导致线程暂停的同时,并不会释放任何锁资源
。
此时,其他任何线程想要访问被它占用的锁时,都会被牵连,导致无法正常继续运行(如图所示)。
直到对应的线程上进行了 resume 方法操作,被挂起的线程才能继续
,从而其他所有阻塞在相关锁上的线程也可以继续执行。
但是,如果 resume 方法操作意外地在 suspend 方法前就执行了,那么被挂起的线程可能很难有机会被继续执行。
并且,更严重的是:它所占用的锁不会被释放,因此可能会导致整个系统工作不正常。
而且,对于被挂起的线程,从它的线程状态上看,居然还是 Runnable ,这也会严重影响我们对系统当前状态的判断。
源码解读(JDK8)
/**
* 挂起此线程。
*
* 首先,调用此线程的checkAccess方法时不带参数。这可能导致抛出SecurityException(在当前线程中)。
*
* 如果线程处于活跃状态,它将被挂起,并且在恢复之前不会取得进一步的进展。
*
* @exception SecurityException–如果当前线程无法修改此线程。
*
* @deprecated 这个方法已经被弃用了,因为它天生就容易死锁。
* 如果目标线程被挂起的时候在监视器上持有一个锁,用来保护关键的系统资源,则在目标线程恢复之前,任何线程都不能访问该资源。
* 如果要恢复目标线程的线程在调用resume之前尝试锁定此监视器,则会导致死锁。
* 这种死锁通常表现为“冻结”进程。
*/
@Deprecated
public final void suspend() {
checkAccess();
suspend0();
}
private native void suspend0();
/**
* 恢复挂起的线程。
*
* 首先,调用此线程的checkAccess方法时不带参数。这可能导致抛出SecurityException(在当前线程中)。
*
* 如果线程处于活动状态,但处于挂起状态,则会恢复该线程,并允许其在执行过程中取得进展。
*
* @exception SecurityException–如果当前线程无法修改此线程。
*
* @deprecated 此方法只存在于与suspend一起使用时,suspend已被弃用,因为它容易死锁。
*/
@Deprecated
public final void resume() {
checkAccess();
resume0();
}
private native void resume0();