前言

本文隶属于专栏《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();
上一篇 下一篇