首页 如何正确停止线程
文章
取消

如何正确停止线程

通知线程中断

1
Thread.currentThread().interrupt();

子线程被阻塞仍然可以感受到中断

当子线程内部被sleep()和wait()阻塞的时候,仍然可以感受到中断信号并且抛出java.lang.InterruptedException异常

子线程如何正确响应中断

对下面的子线程内部方法,如何让子线程正确响应中断:

1
2
3
4
5
6
7
8
9
10
11
12
13
void subTas() {

    try {

        Thread.sleep(1000);

    } catch (InterruptedException e) {

        // 在这里不处理该异常是非常不好的

    }

}

向上面这样,对阻塞逻辑进行try/catch,不过这样子往往是不够的,通常有两种最佳处理方式正确响应中断

  1. 第一种方式:子线程抛出异常,让被调用者主动处理异常,或者层层抛,直到在run()中通过try/catch处理异常
  2. 第二种方法:在catch语句块中调用Thread.currentThread().interrupt()再次中断线程,因为如果线程在休眠期间被中断,那么会自动清除中断信号。如果这时手动添加中断信号,中断信号依然可以被捕捉到。这样后续执行的方法依然可以检测到这里发生过中断,可以做出相应的处理,整个线程可以正常退出。

如果不采用上述方法,我们通常称之为“屏蔽了中断请求”,如果我们盲目地屏蔽了中断请求,会导致中断信号被完全忽略,最终导致线程无法正确停止。

为什么用 volatile 标记位的停止方法是错误的

stop(),suspend() 和 resume(),这些方法已经被 Java 直接标记为 @Deprecated。它们不再推荐使用

  1. 比如stop()会直接停止线程,没有给线程足够时间来处理想要在停止前保存数据的逻辑,任务戛然而止,会导致出现数据完整性等问题。
  2. 对于suspend()和resume()而言,suspend会让线程休眠,但不会释放锁,这样就容易造成死锁问题,因为这把锁在线程被 resume() 之前,是不会被释放的。假设线程A调用suspend()是线程B休眠,而线程B恰好持有一把锁,此时假设线程 A 想访问线程 B 持有的锁,但由于线程 B 并没有释放锁就进入休眠了,所以对于线程 A 而言,此时拿不到锁,也会陷入阻塞,那么线程 A 和线程 B 就都无法继续向下执行。

在某些情况下,volatile是可以正常中断线程的,因为volatile标记的变量是线程在主存上共享的,所以当线程A改变volatile修饰的变量,线程B中volatile修饰的变量也会改变,因此它可以作为标记位

但是当线程遇上阻塞队列,比如生产者,一旦线程阻塞,即使标记位发生变化,线程也没办法去判断了,因为线程已经阻塞了

补充

  1. 中断是干什么的?

中断是为了结束某个线程而发出的信号,中断能够唤醒sleep、wait阻塞下的线程

  1. 为了能够捕获到InterruptedException异常,我们应该对阻塞逻辑进行try/catch
本文由作者按照 CC BY 4.0 进行授权