中断的本质
在Java中,停止一个线程的主要机制是中断,中断并不是强迫终止一个线程,它是一种协作机制,是给线程传递一个取消信号,但是由线程来决定如何以及何时退出。
public boolean isInterrupted() public void interrupt() public static boolean interrupted()
每个线程都有一个标志位,表示该线程是否被中断了。
isInterrupted:返回对应线程的中断标志位是否为true。
interrupted:返回当前线程的中断标志位是否为true,但它还有一个重要的副作用,就是清空中断标志位,也就是说,连续两次调用interrupted(),第一次返回的结果为true,第二次一般就是false(除非同时又发生了一次中断)。
interrupt:表示中断对应的线程。中断具体意味着什么呢?下面我们进一步来说明。
不同状态的线程对中断interrupt()的反应
线程状态 | 对中断的反应 | 线程应该做什么 |
RUNNABLE | 如果线程在运行中,且没有执行IO操作,interrupt()只是会设置线程的中断标志位,没有任何其他作用。 | 线程应该在运行过程中合适的位置检查中断标志位,比如,如果主体代码是一个循环,可以在循环开始处进行检查。 |
WAITING/TIMED_WAITING | 线程调用join/wait/sleep方法会进入WAITING或TIMED_WAITING状态,在这些状态时,对线程对象调用interrupt()会使得该线程抛出InterruptedException。 注意:抛出异常后,中断标志位会被清空,而不是被设置。 | InterruptedException是一个受检异常,线程必须进行处理。我们在异常处理中介绍过,处理异常的基本思路是:如果知道怎么处理,就进行处理,如果不知道,就应该向上传递,通常情况下不应该捕获异常然后忽略。 捕获到InterruptedException,通常表示希望结束该线程,线程大致有两种处理方式:
|
BLOCKED | 如果线程在等待锁,对线程对象调用interrupt()只是会设置线程的中断标志位,线程依然会处于BLOCKED状态,也就是说,interrupt()并不能使一个在等待锁的线程真正“中断”。 在使用synchronized关键字获取锁的过程中不响应中断请求,这是synchronized的局限性。如果这对程序是一个问题,应该使用显式锁。 | 改用 |
NEW/TERMINATE | 如果线程尚未启动(NEW),或者已经结束(TERMINATED),则调用interrupt()对它没有任何效果,中断标志位也不会被设置。 | / |
如何正确地取消/关闭线程
interrupt方法不一定会真正“中断”线程,它只是一种协作机制,如果不明白线程在做什么,不应该贸然地调用线程的interrupt方法,以为这样就能取消线程。
对于以线程提供服务的程序模块而言,它应该封装取消/关闭操作,提供单独的取消/关闭方法给调用者,外部调用者应该调用这些方法而不是直接调用interrupt。
作为线程的实现者,应该提供明确的取消/关闭方法,并用文档描述清楚其行为;作为线程的调用者,应该使用其取消/关闭方法,而不是贸然调用interrupt。
可以参考 ExecutorService 的 shutdown() shutdownNow() 等实现。
#并发编程##java原理#知其然知其所以然,只有了解了底层原理,借助第一性原理,才可以运用自如,成为真大师。 什么是第一性原理? 第一性原理最早由亚里士多德提出,他将其定义为:“事物被已知的第一项前提。” 简单来说,它要求你不要用“类比”去思考(即:因为别人这样做,或者以前这样做,所以我也这样做),克服从众心理(FOMO)和经验偏差,在科技创新、商业决策中找到成本与效率的最优解。