InterruptedException和中断线程的解释

如果没有将InterruptedException检查为异常,则可能甚至没人会注意到它–实际上,这些年来可以防止出现几个错误。 但是由于必须对其进行处理,因此许多人不正确或不加考虑地处理它。 让我们以一个线程的简单示例为例,该线程定期进行一些清理,但大多数情况下在两次睡眠之间进行。

class Cleaner implements Runnable {Cleaner() {final Thread cleanerThread = new Thread(this, "Cleaner");cleanerThread.start();}@Overridepublic void run() {while(true) {cleanUp();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}private void cleanUp() {//...}}

此代码在很多层上都是错误的!

  1. 在某些环境中,在构造函数中启动Thread可能不是一个好主意,例如,某些框架(如Spring)将创建动态子类来支持方法拦截。 最后,我们将从两个实例运行两个线程。
  2. 吞下了InterruptedException ,并且异常本身未正确记录
  3. 此类为每个实例启动一个新线程,它应改用ScheduledThreadPoolExecutor ,在许多实例之间共享(更健壮和更有效地使用内存)
  4. 同样,使用ScheduledThreadPoolExecutor我们可以避免自己编写睡眠/工作循环代码,并且还可以切换到固定速率,而不是此处介绍的固定延迟行为。
  5. 最后但并非最不重要的一点是,即使Cleaner实例不再被其他任何东西引用,也没有办法摆脱此线程。

所有问题都是有效的,但是吞没InterruptedException是其最大的罪过。 在我们理解原因之前,让我们先思考一下该异常的含义以及如何利用它来优雅地中断线程。 JDK中的许多阻止操作都声明抛出InterruptedException ,包括:

  • Object.wait()
  • Thread.sleep()
  • Process.waitFor()
  • AsynchronousChannelGroup.awaitTermination()
  • java.util.concurrent.*各种阻塞方法,例如ExecutorService.awaitTermination()Future.get()BlockingQueue.take()Semaphore.acquire() Condition.await()以及许多其他方法
  • SwingUtilities.invokeAndWait()

请注意,阻塞I / O不会引发InterruptedException (这很可惜)。 如果所有这些类都声明了InterruptedException ,那么您可能想知道何时会抛出此异常?

  • 当某个线程在声明InterruptedException某个方法上被阻塞并且您在该线程上调用Thread.interrupt()时,很可能阻塞的方法将立即抛出InterruptedException
  • 如果您将任务提交到线程池( ExecutorService.submit() ),并且在执行任务时调用Future.cancel(true) 。 在这种情况下,线程池将尝试为您中断正在运行此类任务的线程,从而有效地中断了您的任务。

知道InterruptedException实际含义后,我们就可以正确处理它。 如果有人试图中断我们的线程,而我们通过捕获InterruptedException发现了它,则最合理的做法是让该线程完成,例如:

class Cleaner implements Runnable, AutoCloseable {private final Thread cleanerThread;Cleaner() {cleanerThread = new Thread(this, "Cleaner");cleanerThread.start();}@Overridepublic void run() {try {while (true) {cleanUp();TimeUnit.SECONDS.sleep(1);}} catch (InterruptedException ignored) {log.debug("Interrupted, closing");}}//...   @Overridepublic void close() {cleanerThread.interrupt();}
}

注意, try-catch块现在围绕while循环。 这样,如果sleep()抛出InterruptedException ,我们将跳出循环。 您可能会争辩说,我们应该记录InterruptedException的堆栈跟踪。 这取决于情况,因为在这种情况下中断线程是我们真正希望的,而不是失败。 但这取决于你。 最重要的是,如果sleep()被另一个线程中断,我们将很快完全脱离run() 。 如果您非常小心,您可能会问,如果线程在cleanUp()方法中而不是在睡眠时中断线程,会发生什么情况? 通常,您会遇到这样的手动标记:

private volatile boolean stop = false;@Override
public void run() {while (!stop) {cleanUp();TimeUnit.SECONDS.sleep(1);}
}@Override
public void close() {stop = true;
}

但是请注意, stop标志(必须是volatile !)不会中断阻塞操作,我们必须等到sleep()完成。 另一方面,有人可能会争辩说,显式flag可以更好地控制我们,因为我们可以随时监视其值。 事实证明,线程中断的工作方式相同。 如果有人在执行非阻塞计算时中断了线程(例如在cleanUp()内部),则此类计算不会立即中断。 但是,线程被标记为已中断,并且随后的所有阻塞操作(例如sleep() )都将立即立即抛出InterruptedException因此我们不会丢失该信号。

如果我们编写仍想利用线程中断功能的非阻塞线程,那么我们也可以利用这一事实。 不必依赖于InterruptedException我们只需定期检查Thread.isInterrupted()

public void run() {while (Thread.currentThread().isInterrupted()) {someHeavyComputations();}
}

在上方,如果有人中断了我们的线程,则在someHeavyComputations()返回时我们将立即放弃计算。 如果它运行了两个长时间或无限期,我们将永远不会发现中断标志。 有趣的是, interrupted标志不是一次性的 。 我们可以调用Thread.interrupted()而不是isInterrupted() ,这将重置interrupted标志并且我们可以继续。 有时您可能想忽略中断标志并继续运行。 在这种情况下, interrupted()可能会派上用场。 顺便说一句,我(不精确地)称“吸气剂”来改变被观察物体的状态 “ 海森格 ”。

注意

如果您是老派程序员,则可能会想起Thread.stop()方法,该方法已被弃用10年了 。 在Java 8中,有计划“取消实现” ,但是在1.8u5中,它仍然存在。 但是,不要使用它,而是使用Thread.stop()将任何代码重构为Thread.interrupt()

番石榴的

很少您可能会完全忽略InterruptedException 。 在这种情况下,请查看Guava的Uninterruptibles 。 它具有许多实用方法,例如sleepUninterruptibly()awaitUninterruptibly(CountDownLatch) 。 只是要小心他们。 我知道他们没有声明InterruptedException (可能很少),但是它们也完全防止了当前线程被中断–这是非常不寻常的。

摘要

到现在为止,我希望您对为什么某些方法引发InterruptedException有所了解。 主要的收获是:

  • 捕获的InterruptedException应该得到正确处理-大多数情况下,这意味着完全脱离当前任务/循环/线程
  • 吞下InterruptedException很少是一个好主意
  • 如果线程不在阻塞调用中时被中断,请使用isInterrupted() 。 当线程已经被中断时也进入阻塞方法应该立即抛出InterruptedException

翻译自: https://www.javacodegeeks.com/2014/06/interruptedexception-and-interrupting-threads-explained.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/341381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pat 乙级 1020 月饼(C++实现)

题目 月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请你计算可以获得的最大收益是多少。 注意:销售时允许取出一部分库存。样例给出的情形是这样的&…

Python的from和import用法

import使一个变量名引用整个模块对象,因此必须通过模块名称来得到该模块的属性,比如我们导入一个数学计算的模块 math: import mathprint math.pi #导出圆周率的值>>>3.14159265359我们导入math模块,在python模块学习中我们会知道&a…

带有Spring Cloud Config和JHipster的Java微服务

朋友不允许朋友写用户身份验证。 厌倦了管理自己的用户? 立即尝试Okta的API和Java SDK。 在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护。 如今,使用Java和Spring Boot开发微服务架构非常流行。 它绝对是Java生态系统中最…

pat 乙级 1026 程序运行时间(C++)

题目 要获得一个 C 语言程序的运行时间,常用的方法是调用头文件 time.h,其中提供了 clock() 函数,可以捕捉从程序开始运行到 clock() 被调用时所耗费的时间。这个时间单位是 clock tick,即“时钟打点”。同时还有一个常数 CLK_TC…

什么是命名空间 为什么C++需要使用命名空间

引言&#xff1a; 问题&#xff1a;学习过C&#xff0c;然后再学c的人可能会这么问&#xff0c;我#include<iostream>之后&#xff0c;不就是把头文件拉进来了吗&#xff0c;而cin cout等函数不就是iostream.h里面的函数吗&#xff0c;我使用cin cout时&#xff0c;编译器…

pat 乙级 1023 组个最小数(C++)

题目 给定数字 0-9 各若干个。你可以以任意顺序排列这些数字&#xff0c;但必须全部使用。目标是使得最后得到的数尽可能小&#xff08;注意 0 不能做首位&#xff09;。例如&#xff1a;给定两个 0&#xff0c;两个 1&#xff0c;三个 5&#xff0c;一个 8&#xff0c;我们得…

C++谷歌命名规范

1.文件命名规则 文件名全部小写&#xff0c;可以含下划线或连字符&#xff0c;按项目约定命名,且尽量保证文件名明确。比如&#xff1a;cmd_save_player_info_class.cc &#xff0c;my_use_full_class.cc 定义类的文件名一般是成对出现&#xff0c;如&#xff1a;foo_bar.h f…

pat 乙级 1028 人口普查(C++)

题目 某城镇进行人口普查&#xff0c;得到了全体居民的生日。现请你写个程序&#xff0c;找出镇上最年长和最年轻的人。 这里确保每个输入的日期都是合法的&#xff0c;但不一定是合理的——假设已知镇上没有超过 200 岁的老人&#xff0c;而今天是 2014 年 9 月 6 日&#xf…

c++ 为什么要按它们声明的顺序初始化成员变量?

我今天正在写一些代码&#xff0c;并得到一个奇怪的编译错误&#xff0c;这似乎是由初始化成员变量的顺序不同于他们声明的。 class Test {int a;int b;public:Test() : b(1), a(2) {} };int main() {Test test;return 0; } 然后&#xff0c;如果我编译它与-Werror -Wall&…

如何在Java中使用Lombok删除样板安装程序获取器

你好朋友&#xff0c; 一次又一次反对Java的观点之一是&#xff0c;我们必须写很多样板 我们简单的POJO类的setter和getter形式的代码&#xff0c;不必要地增加了 我们代码的长度。 为了解决这个问题&#xff0c;有一个名为Project Lombok的开源项目可以解决这个问题。 通过…

Python 列表list与数组array的区别

1. 列表list与数组array的定义&#xff1a; 列表是由一系列按特定顺序排列的元素组成&#xff0c;可以将任何东西加入列表中&#xff0c;其中的元素之间没有任何关系&#xff1b; Python中的列表(list)用于顺序存储结构。它可以方便、高效的的添加删除元素&#xff0c;并且列…

Dev-C++使用技巧1(亲测)(超详细)(安装过程、修改语言、新建项目、配置C++ 11、开启警告信息和调试信息)

前言 Dev-C是一款内存占用小&#xff0c;支持C/C&#xff0c;支持调试&#xff0c;可以一键编译、运行的IDE&#xff08;集成开发环境&#xff09;。 下载链接&#xff1a;Dev-C - Download 如果上面那个不行或者下载比较慢&#xff0c;可以点链接: Dev-Cpp 提取码: xaby 。 …

python列表各元素修改为int类型

import numpy as np action np.trunc(action).astype(int).tolist() 函数去掉小数&#xff0c;但元素本身并不是整数。&#xff08; 例1. 或者 2. 没有小数&#xff0c;但也不是整数&#xff09; 要保证元素本身是int类型&#xff0c;则使用astype() &#xff08;得到 [ 1 2…

pat 乙级 1029 旧键盘(C++)

题目 旧键盘上坏了几个键&#xff0c;于是在敲一段文字的时候&#xff0c;对应的字符就不会出现。现在给出应该输入的一段文字、以及实际被输入的文字&#xff0c;请你列出肯定坏掉的那些键。 输入格式&#xff1a; 输入在 2 行中分别给出应该输入的文字、以及实际被输入的文…

1.0jpa 2.0_JPA 2.1类型转换器–持久枚举的更好方法

1.0jpa 2.0可以使用JPA 2.0保留枚举&#xff0c;但是没有很好的方法来实现。 使用Enumerated批注&#xff0c;可以使用EnumType.ORDINAL或EnumType.STRING将枚举值映射到其数据库表示形式。 但是这两种选择都有一些缺点&#xff0c;我们将在本文的第一部分中进行讨论。 在第二部…

从网络虚拟化,看智能网卡发展史

5G的到来无疑将加速网络虚拟化的进程&#xff0c;在电信领域&#xff0c;网络虚拟化不仅在核心网&#xff0c;也在网络的边缘。但是&#xff0c;仅仅通过软件解决方案不能提供足够的网络可靠性和服务质量&#xff0c;而具有高级可编程功能智能网卡&#xff08;Smart NIC&#x…

Dev-C++使用技巧2(亲测)(更改字体和颜色、自动保存、快捷键选项、一键排版)

更改字体和颜色 顶部菜单栏->工具->编辑器选项。 更改字体 当前选项卡->显示&#xff0c;字体word有的基本都有&#xff0c;华文楷体、宋体&#xff0c;幼圆之类。西文字体默认字体Consloas就不错&#xff0c;中文的话幼圆、新宋体都挺不错。 幼圆效果&#xff1…

Java恶意序列化背后的历史和动机

与Java的序列化机制有关的问题已广为人知。 有效的Java 1st Edition &#xff08;第10章&#xff09;和有效的Java 2nd Edition &#xff08;第11章&#xff09;的整个最后一章都专门讨论Java的序列化主题。 Effective Java 3rd Edition &#xff08;第12章&#xff09;的最后一…

深入浅出全面解析RDMA

RDMA(RemoteDirect Memory Access)技术全称远程直接内存访问&#xff0c;就是为了解决网络传输中客户端与服务器端数据处理的延迟而产生的。它将数据直接从一台计算机的内存传输到另一台计算机&#xff0c;无需双方操作系统的介入。这允许高吞吐、低延迟的网络通信&#xff0c;…

pat 乙级 1030 完美数列(C++)

题目 给定一个正整数数列&#xff0c;和正整数 p&#xff0c;设这个数列中的最大值是 M&#xff0c;最小值是 m&#xff0c;如果 M≤mp&#xff0c;则称这个数列是完美数列。 现在给定参数 p 和一些正整数&#xff0c;请你从中选择尽可能多的数构成一个完美数列。 输入格式&…