怎样处理InterruptedException

Java 中的受检查异常 InterruptedException 如何处理是令人头痛的问题,下面是我对处理这个问题的理解。

Java 中的 InterruptedException 一直是一个令人头疼的问题,对初级开发者来说尤其如此。但实际上不应如此,这其实是一个很容易理解的问题。我会尽可能简单地描述这个问题。

我们从这段代码开始:

它做了什么?什么都没做,只是无止境的消耗 CPU。我们能终止它吗?在 Java 中是不行的。只有当你按下 Ctrl-C 来终止整个 JVM 时这段程序才会停止。在 Java 中没有方式来终止一个线程,除非该线程自动退出。请务必牢记的这一原则,其它东西就显而易见了。

我们将这个死循环放在一个线程里:

所以,怎样才能停止一个需要停止的线程?

下面是 Java 中设计终止一个线程的方法。在线程的外部,设置一个标识变量(flag),然后在线程内部检查改标识变量,从而实现线程的终止。过程如下:

这是终止线程的唯一方式,在这个例子里使用了两个方法。当调用 loop.interrupt() ,线程内部将标志位设置为 true。当调用 interrupted() 时,立即返回,并将标识变量设置为 false。确实,这个方法就是这样设计的。检查标识变量、返回、设置为 false。我知道这很丑陋。

因此,我从来没有在线程内调用 Thread.interrupted() 方法,因此标识变量为 true 时线程不会退出,没有人能停止这个线程。准确地说,我会忽略他们对 interrupt() 方法的调用。虽然它们会要求终止线程,但是我会忽略它们。它们不能让线程中断。

因此,总结一下我们现在理解的内容,一种合理的设计是通过检查标识变量来优雅地终止线程。如果代码中不检测标识变量,也不调用 Thread.interrupted(),那么终止线程的方式就只能按下 Ctrl-C 了。

现在你听明白这个逻辑了吗?我希望是。

现在,JDK 中有一些方法来检测标识变量,如果设置该标识变量,则会抛出 InterruptedException。例如,Thread.sleep() 方法的设计(一种最基本的方法):

为什么要这么做?为什么不能等待并且不用去检查标识变量?我相信一定有一个非常好的理由。理由如下(如果我说错了,请修正我的错误):为了让代码变快或是中断准备,没有其他理由。

如果你的代码足够快,你从来不会检测中断标识变量,因为你不想处理任何中断。如果你代码很慢,可能需要执行数秒,这时你就有可能需要处理中断了。

这就是为什么 InterruptedException 是受检查异常。这种设计告诉你,如果你想在几毫秒内停止线程,确定你已经做好中断准备。实践中一般做如下处理:

现在,你可以将它抛给负责捕获该异常的上级程序去处理。这种观点是有人在使用线程,并且会捕获该异常。理想情况下,会终止线程,因为这就是标识变量的功能。如果抛出 InterruptedException,就意味着有人在检查标识变量,线程需要尽可能快地终止。

线程的拥有者不想再等待线程执行,我们应该尊重拥有者的决定。

因此,当捕获到 InterruptedException 时,你应该完成相关的操作再退出线程。

现在,我们再看一下 Thread.sleep() 的代码:

请记住,Thread.interrupted() 不仅仅是返回标识变量的值,而且会将标识变量的值设置为 false。因此,一旦抛出 InterruptedException 异常,标志变量将会重置。线程不再收到任何拥有者发送的中断请求。

线程的所有者要求停止线程,Thread.sleep() 监测到该请求并将其删除,再抛出 InterruptedException。如果你再次调用 Thread.sleep(),就不会响应任何中断请求,也不会抛出任何异常。

知道我想要说的是什么吗?不要丢失 InterruptedException,这一点非常重要。我们不能吞噬该异常并继续运行。这严重违背了 Java 多线程原则。所有者(线程的所有者)要求停止线程,而我们却将其忽略,这是非常不好的想法。

下面是大多数人对 InterruptedException 的处理:

这看起来是符合逻辑的,但是这不能保证上层程序真正停止并退出。上层可能捕获了运行时异常,所以这个线程还是存活的。线程所有者将会非常失望。

我们必须通知上层捕获了一个中断请求。我们不能只抛出运行时异常,这种行为太不负责了。当一个线程接收一个中断请求时,我们不能只是将其转换成为一个 RuntimeException。我们不能将这种严峻的情况如此轻松地对待。

这是我们应该做的:

我们需要将标识变量重新设置为 true。

现在,没有人会谴责我们以不负责的态度来处理标识变量。我们发现其状态为 true,将其清理,重新设置为 true,最后抛出运行时异常。接下来会发生什么?我们已经不关心了。

这就是我认为的处理方式。你可以找到这个问题更详细的官方描述:Java 理论与实践:InterruptedException 处理

原文链接: https://dzone.com/articles/how-to-handle-the-interruptedexception 翻译:ImportNew.com - paddx

原文发布于微信公众号 - java一日一条(mjx_java)

原文发表时间:2015-11-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程

java编程思想之并发

编程问题中的相当一大部分都可以通过顺序编程来解决。然而,对于某些问题,如果能够并行的执行程序中的多个部分,则会变得非常方便甚至非常必要,这些部分要么可以并发执行...

2087
来自专栏软件开发 -- 分享 互助 成长

注意map<> 的[]

其实在之前一篇关于map的基本操作中已经提到过注意[]操作,这里再强调一下。 先看下面的程序: #include<iostream> #include<map>...

1726
来自专栏java技术学习之道

Java线程面试题合集(含答案)

1964
来自专栏java思维导图

Java 9 新特性,看这里就明白了

摘要: 1.目录结构 2.repl工具 jShell命令 3.模块化 4.多版本兼容jar包 5.接口方法的改进(在接口中 jdk7 只能声明全名常量...

3266
来自专栏企鹅号快讯

一文学会Python协程

Python圣诞学习狂欢夜 距离开始还有3天 . . . 详情 . . . 生成器和协程的介绍 生成器(Generator)的本质和特点 生成器 是 可以生成一...

28710
来自专栏精讲JAVA

Java 9 新特性,看这里就明白了

摘要: 1.目录结构 2.repl工具 jShell命令 3.模块化 4.多版本兼容jar包 5.接口方法的改进(在接口中 jdk7 只能声明全名常量...

3676
来自专栏程序员的碎碎念

php写接口入门

了解JSON JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation) JSON 是轻量级的文本数据交换格式 ...

1.4K8
来自专栏Android常用基础

粗谈Java虚拟机之原理与结构

在学习java虚拟机之前,我们有必要先来了解下下class文件与dex文件。相比大家对这两文件都耳熟能详,但是对于初学者来说却是"听起声而不见其人"。下面我们就...

911
来自专栏极客编程

基于Node.js的自动化工具Gulp

gulp是前端开发过程中一种基于流的代码构建工具,是自动化项目的构建利器;她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成;...

1731
来自专栏Golang语言社区

go语言的官方包sync.Pool的实现原理和适用场景

已经使用golang有一段时间,go的协程和gc垃圾回收特性的确会提高程序的开发效率。但是毕竟是一门新语言,如果对于它的机制不了解,用起来可能会蹦出各种潘多拉盒...

1K6

扫码关注云+社区

领取腾讯云代金券