中断的本质

在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,通常表示希望结束该线程,线程大致有两种处理方式:

  1. 向上传递该异常,这使得该方法也变成了一个可中断的方法,需要调用者进行处理;
  2. 有些情况,不能向上传递异常,比如Thread的run方法,它的声明是固定的,不能抛出任何受检异常,这时,应该捕获异常,进行合适的清理操作,清理后,一般应该调用Thread的interrupt方法设置中断标志位 Thread.currentThread().interrupt(); ,使得其他代码有办法知道它发生了中断。

BLOCKED

如果线程在等待锁,对线程对象调用interrupt()只是会设置线程的中断标志位,线程依然会处于BLOCKED状态,也就是说,interrupt()并不能使一个在等待锁的线程真正“中断”​。

在使用synchronized关键字获取锁的过程中不响应中断请求,这是synchronized的局限性。如果这对程序是一个问题,应该使用显式锁。

改用 ReentrantLock

NEW/TERMINATE

如果线程尚未启动(NEW)​,或者已经结束(TERMINATED)​,则调用interrupt()对它没有任何效果,中断标志位也不会被设置。

/

如何正确地取消/关闭线程

interrupt方法不一定会真正“中断”线程,它只是一种协作机制,如果不明白线程在做什么,不应该贸然地调用线程的interrupt方法,以为这样就能取消线程。

对于以线程提供服务的程序模块而言,它应该封装取消/关闭操作,提供单独的取消/关闭方法给调用者,外部调用者应该调用这些方法而不是直接调用interrupt。

作为线程的实现者,应该提供明确的取消/关闭方法,并用文档描述清楚其行为;作为线程的调用者,应该使用其取消/关闭方法,而不是贸然调用interrupt。

可以参考 ExecutorService 的 shutdown() shutdownNow() 等实现。

#并发编程##java原理#
27届毕业生-Java知识专辑 文章被收录于专栏

知其然知其所以然,只有掌握了底层原理,借助第一性原理,才可以在日常开发和项目中运用自如,潇洒走江湖。 专为27届毕业生准备,托起您的就业梦。 该专辑会不定时更新,建议27届同学订阅,入职后扎实的基本功可以帮您争取更好的机会和项目。

全部评论
有一道关于中断的面试题,看看大家是否知道答案:https://www.nowcoder.com/discuss/859405187675377664
点赞 回复 分享
发布于 今天 12:14 北京

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务