一,介绍

这篇文章主要记录使用 interrupt() 方法中断线程,以及如何对InterruptedException进行处理。感觉对InterruptedException异常进行处理是一件谨慎且有技巧的活儿。

由于使用stop()方法停止线程非常的暴力,人家线程运行的好好的,突然就把人家杀死了,线程占用的锁被强制释放,极易导致数据的不一致性。可参考这篇文章对stop()方法的介绍。

因此,提出了一种温和的方式:请求另外一个线程不要再执行了,这就是中断方式。

二,中断及如何响应中断?

如何优雅地响应中断真的是太高深了,看到这篇文章:Java 理论与实践: 处理 InterruptedException就吓了一跳。下面只是记录一些最简单的我对响应中断的理解。

假设某个线程要不停地处理某件事情(比如 i 一直自增),但是还有个要求:在处理事情前,先要检查下这个线程是否被中断,如果已经被中断,处理就应该结束。

下面是一些例子,这些例子摘自书本:

 public class Run {

     public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(20);//modify 2000 to 20
thread.interrupt();//请求中断MyThread线程
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}

main线程睡眠20ms后,执行第8行中断MyThread线程。

 public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) {
System.out.println("should be stopped and exit");
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("this line is also executed. thread does not stopped");//尽管线程被中断,但并没有结束运行。这行代码还是会被执行
}
}

当MyThread获得CPU执行时,第6行的 if 测试中,检测到中断标识被设置。即MyThread线程检测到了main线程想要中断它的 请求。

大多数情况下,MyThread检测到了中断请求,对该中断的响应是:退出执行(或者说是结束执行)。

但是,上面第5至8行for循环,是执行break语句跳出for循环。但是,线程并没有结束,它只是跳出了for循环而已,它还会继续执行第12行的代码....

因此,我们的问题是,当收到了中断请求后,如何结束该线程呢?

一种可行的方法是使用 return 语句 而不是 break语句。。。。。哈哈。。。

当然,一种更优雅的方式则是:抛出InterruptedException异常。

看下面MyThread类的代码:

 public class MyThread extends Thread {
@Override
public void run() {
super.run();
try{
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) {
System.out.println("should be stopped and exit");
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("this line cannot be executed. cause thread throws exception");//这行语句不会被执行!!!
}catch(InterruptedException e){
System.out.println("catch interrupted exception");
e.printStackTrace();
}
}
}

当MyThread线程检测到中断标识为true后,在第9行抛出InterruptedException异常。这样,该线程就不能再执行其他的正常语句了(如,第13行语句不会执行)。这里表明:interrupt()方法有两个作用,一个是将线程的中断状态置位(中断状态由false变成true);另一个则是:让被中断的线程抛出InterruptedException异常。

这是很重要的。这样,对于那些阻塞方法(比如 wait() 和 sleep())而言,当另一个线程调用interrupt()中断该线程时,该线程会从阻塞状态退出并且抛出中断异常。这样,我们就可以捕捉到中断异常,并根据实际情况对该线程从阻塞方法中异常退出而进行一些处理。

比如说:线程A获得了锁进入了同步代码块中,但由于条件不足调用 wait() 方法阻塞了。这个时候,线程B执行 threadA.interrupt()请求中断线程A,此时线程A就会抛出InterruptedException,我们就可以在catch中捕获到这个异常并进行相应处理(比如进一步往上抛出)

因此,上面就是一个采用抛出异常的方式来结束线程的示例。尽管该示例的实用性不大。原因在 IBM的这篇博文中:我们 生吞了中断。

在第14行,我们直接catch了异常,然后打印输出了一下而已,调用栈中的更高层的代码是无法获得关于该异常的信息的。

第16行的e.printStackTrace()作用就相当于

“(仅仅记录 InterruptedException 也不是明智的做法,因为等到人来读取日志的时候,再来对它作出处理就为时已晚了。)”---摘自参考博文

上面我们是在run()方法中抛出异常,符合这里描述的:

有时候抛出 InterruptedException 并不合适,例如当由 Runnable 定义的任务调用一个
可中断的方法时,就是如此。在这种情况下,不能重新抛出 InterruptedException,但是
您也不想什么都不做。当一个阻塞方法检测到中断并抛出 InterruptedException 时,它
清除中断状态。如果捕捉到 InterruptedException 但是不能重新抛出它,那么应该保留
中断发生的证据,以便调用栈中更高层的代码能知道中断,并对中断作出响应。该任务可以
通过调用 interrupt() 以 “重新中断” 当前线程来完成,如清单 3 所示。 -----“摘自参考博文”

因为,run方法是实现的Runnable接口中的方法。不能像下面这样定义,也即上面所说的:“不能重新抛出InterruptedException”。

        @Override
public void run() throws InterruptedException{//这是错误的
//do something...

因此,一个更好的解决方案是:调用 interrupt() 以 “重新中断” 当前线程。改进MyThread类中catch异常的方式,如下:

 public class MyThread extends Thread {
@Override
public void run() {
super.run();
try{
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) {
System.out.println("should be stopped and exit");
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("this line cannot be executed. cause thread throws exception");
}catch(InterruptedException e){
/**这样处理不好
* System.out.println("catch interrupted exception");
* e.printStackTrace();
*/
Thread.currentThread().interrupt();//这样处理比较好
}
}
}

这样,就由 生吞异常 变成了 将 异常事件 进一步扩散了。

参考博文:Java 理论与实践: 处理 InterruptedException

参考书籍:《Java多线程编程核心技术》

JAVA多线程之中断机制(如何处理中断?)的更多相关文章

  1. JAVA多线程之中断机制(stop()、interrupted()、isInterrupted())

    一,介绍 本文记录JAVA多线程中的中断机制的一些知识点.主要是stop方法.interrupted()与isInterrupted()方法的区别,并从源代码的实现上进行简单分析. JAVA中有3种方 ...

  2. java多线程-概念&创建启动&中断&守护线程&优先级&线程状态(多线程编程之一)

    今天开始就来总结一下Java多线程的基础知识点,下面是本篇的主要内容(大部分知识点参考java核心技术卷1): 1.什么是线程以及多线程与进程的区别 2.多线程的创建与启动 3.中断线程和守护线程以及 ...

  3. Java多线程——中断机制

    前言:在Java多线程中,中断一直围绕着我们,当我们阅读各种关于Java多线程的资料.书籍时,“中断”一词总是会出现,笔者对其的理解也是朦朦胧胧,因此非常有必要搞清楚Java多线程的中断机制. 1.J ...

  4. 50个Java多线程面试题

    不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java 语言一个重要的特点就是内置了对并发的支持,让 Java 大受企业和程序员的欢迎.大多数待遇丰厚的 Java 开发职位都要求开发者 ...

  5. 50个Java多线程面试题(上)

    Java 语言一个重要的特点就是内置了对并发的支持,让 Java 大受企业和程序员的欢迎.大多数待遇丰厚的 Java 开发职位都要求开发者精通多线程技术并且有丰富的 Java 程序开发.调试.优化经验 ...

  6. java多线程面试题(来自转载)

    在典型的Java面试中, 面试官会从线程的基本概念问起, 如:为什么你需要使用线程, 如何创建线程,用什么方式创建线程比较好(比如:继承thread类还是调用Runnable接口),然后逐渐问到并发问 ...

  7. Java多线程与并发面试题

    1,什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成一 ...

  8. Java多线程面试题整理

    部分一:多线程部分: 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速. ...

  9. JAVA多线程17个问题

    1.Thread 类中的start() 和 run() 方法有什么区别? Thread.start()方法(native)启动线程,使之进入就绪状态,当cpu分配时间该线程时,由JVM调度执行run( ...

随机推荐

  1. HTML-JS 数组 内置对象

    [JS中的数组] 1.数组的基本概念? 数组是在内存空间中连续存储的一组有序数据的集合 元素在数组中的顺序,称为下标.可以使用下标访问数组的每个元素 2.如何声明一个数组 ① 使用字面量声明:var ...

  2. Js把Json序列化为Java接受的对象。

    服务器端 Java定义 data class role(var name: String = "", var remark: String = "") data ...

  3. hashContext

    java.lnag.Object中对hashCode的约定: 1. 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必 ...

  4. 遇到的eclipse启动报错问题解决

    遇到的eclipse启动报错问题解决 一.启动时出现Java was started but returned exit code=13 可能原因: 1.eclipse与JDK的不是都64位或者32位 ...

  5. github个人心得

    github真难全是英语真费劲 以后要好好学习英语 https://github.com/huangjingyi/test

  6. 广商博客冲刺第六七天new

    第四五天沖刺傳送門 第一版的網頁已經放到 云服務器(估計快到期了) 傳送門. (不怎么會玩服務器啊..求指教..目前問題如下: 1.我的電腦mysql密碼跟服務器的密碼不一樣..上傳的時候要把代碼里面 ...

  7. Windows平台下面Oracle11.2.0.1 升级Oracle11.2.0.4 的简单步骤

    1. 首先查看数据库的版本: 2. ESXi 上面的虚拟机挂在 oracle11.2.0.4的 iso磁盘 3. 执行set 进行升级 4. 安装选项进行选择 升级现有的数据库 5. 注意安装位置必须 ...

  8. TFS2018 linux Agent的安装

    1. 感谢徐蕾老师的文档,根据文档简单学会了TFS agent的安装,在此简单记录一下: 前置条件: CentOS7.4 or CentOS7.5的版本 安装的软件有git 2.17 dotnet s ...

  9. ultraEdit MAC 破解方法

    安装了个UltraEdit 但是需要验证码,太麻烦了,破解方法: 拷贝附件(command +c )然后在MAC的底下点击访达==>应用程序==>UltraEdit==>右击 显示包 ...

  10. Windows server 2008 r2下载地址和激活破解方法

    Windows 7发布了服务器版本——Windows Server 2008 R2.同2008年1月发布的Windows Server 2008相比,Windows Server 2008 R2继续提 ...