通知线程中断
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,不过这样子往往是不够的,通常有两种最佳处理方式正确响应中断
- 第一种方式:子线程抛出异常,让被调用者主动处理异常,或者层层抛,直到在run()中通过try/catch处理异常
- 第二种方法:在catch语句块中调用Thread.currentThread().interrupt()再次中断线程,因为如果线程在休眠期间被中断,那么会自动清除中断信号。如果这时手动添加中断信号,中断信号依然可以被捕捉到。这样后续执行的方法依然可以检测到这里发生过中断,可以做出相应的处理,整个线程可以正常退出。
如果不采用上述方法,我们通常称之为“屏蔽了中断请求”,如果我们盲目地屏蔽了中断请求,会导致中断信号被完全忽略,最终导致线程无法正确停止。
为什么用 volatile 标记位的停止方法是错误的
stop(),suspend() 和 resume(),这些方法已经被 Java 直接标记为 @Deprecated。它们不再推荐使用
- 比如stop()会直接停止线程,没有给线程足够时间来处理想要在停止前保存数据的逻辑,任务戛然而止,会导致出现数据完整性等问题。
- 对于suspend()和resume()而言,suspend会让线程休眠,但不会释放锁,这样就容易造成死锁问题,因为这把锁在线程被 resume() 之前,是不会被释放的。假设线程A调用suspend()是线程B休眠,而线程B恰好持有一把锁,此时假设线程 A 想访问线程 B 持有的锁,但由于线程 B 并没有释放锁就进入休眠了,所以对于线程 A 而言,此时拿不到锁,也会陷入阻塞,那么线程 A 和线程 B 就都无法继续向下执行。
在某些情况下,volatile是可以正常中断线程的,因为volatile标记的变量是线程在主存上共享的,所以当线程A改变volatile修饰的变量,线程B中volatile修饰的变量也会改变,因此它可以作为标记位
但是当线程遇上阻塞队列,比如生产者,一旦线程阻塞,即使标记位发生变化,线程也没办法去判断了,因为线程已经阻塞了
补充
- 中断是干什么的?
中断是为了结束某个线程而发出的信号,中断能够唤醒sleep、wait阻塞下的线程
- 为了能够捕获到InterruptedException异常,我们应该对阻塞逻辑进行try/catch