JAVA多线程之中断机制(如何处理中断?)

一,介绍

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

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

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

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

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

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

下面是一些例子,这些例子摘自《JAVA多线程核心编程技术》

 1 public class Run {
 2
 3     public static void main(String[] args) {
 4         try {
 5             MyThread thread = new MyThread();
 6             thread.start();
 7             Thread.sleep(2000);
 8             thread.interrupt();//请求中断MyThread线程
 9         } catch (InterruptedException e) {
10             System.out.println("main catch");
11             e.printStackTrace();
12         }
13         System.out.println("end!");
14     }
15 }

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

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

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

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

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

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

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

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

看下面MyThread类的代码:

 1 public class MyThread extends Thread {
 2     @Override
 3     public void run() {
 4         super.run();
 5         try{
 6             for (int i = 0; i < 500000; i++) {
 7                 if (this.interrupted()) {
 8                     System.out.println("should be stopped and exit");
 9                     throw new InterruptedException();
10                 }
11                 System.out.println("i=" + (i + 1));
12             }
13             System.out.println("this line cannot be executed. cause thread throws exception");//这行语句不会被执行!!!
14         }catch(InterruptedException e){
15             System.out.println("catch interrupted exception");
16             e.printStackTrace();
17         }
18     }
19 }

当MyThread线程检测到中断标识为true后,在第9行抛出InterruptedException异常。这样,该线程就不能再执行其他的正常语句了(如,第13行语句不会执行)。

因此,上面就是一个采用抛出异常的方式来结束线程的示例。尽管该示例的实用性不大。原因在 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异常的方式,如下:

 1 public class MyThread extends Thread {
 2     @Override
 3     public void run() {
 4         super.run();
 5         try{
 6             for (int i = 0; i < 500000; i++) {
 7                 if (this.interrupted()) {
 8                     System.out.println("should be stopped and exit");
 9                     throw new InterruptedException();
10                 }
11                 System.out.println("i=" + (i + 1));
12             }
13             System.out.println("this line cannot be executed. cause thread throws exception");
14         }catch(InterruptedException e){
15             /**这样处理不好
16              * System.out.println("catch interrupted exception");
17              * e.printStackTrace();
18              */
19              Thread.currentThread().interrupt();//这样处理比较好
20         }
21     }
22 }

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

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

时间: 2024-10-12 13:07:43

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

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

一,介绍 本文记录JAVA多线程中的中断机制的一些知识点.主要是stop方法.interrupted()与isInterrupted()方法的区别,并从源代码的实现上进行简单分析. JAVA中有3种方式可以终止正在运行的线程 ①线程正常退出,即run()方法执行完毕了 ②使用Thread类中的stop()方法强行终止线程.但stop()方法已经过期了,不推荐使用 ③使用中断机制 线程正常退出没有什么东东,中断机制下面详细介绍,先看下stop()方法的源代码,关键是源代码上的注释.它解释了为什么s

Java多线程(2)--线程的中断和中断的控制

如果Java程序不只有一个执行线程,只有当所有线程结束的时候这个程序才能运行结束.更确切的说是所有的非守护线程运行结束的时候,或者其中一个线程调用了System.exet()方法时,程序才运行结束. Java提供了中断机制,我们可以采用它来结束一个线程.我们创建一个线程,使其运行5秒后通过中断机制强制使其终止.程序检查数字是否是质数. package com.concurrency; public class PrimeGenerator extends Thread{ //继承自Thread类

50个Java多线程面试题

不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java 语言一个重要的特点就是内置了对并发的支持,让 Java 大受企业和程序员的欢迎.大多数待遇丰厚的 Java 开发职位都要求开发者精通多线程技术并且有丰富的 Java 程序开发.调试.优化经验,所以线程相关的问题在面试中经常会被提到. 在典型的 Java 面试中, 面试官会从线程的基本概念问起, 如:为什么你需要使用线程, 如何创建线程,用什么方式创建线程比较好(比如:继承 thread 类还是调用 Runnable 接口),

JAVA多线程面试题

1.Thread 类中的start() 和 run() 方法有什么区别? Thread.start()方法(native)启动线程,使之进入就绪状态,当cpu分配时间该线程时,由JVM调度执行run()方法. 当你调用start()方法时你将创建新的线程,并且执行在run()方法里的代码.但是如果你直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码. 2.Java中Runnable和Callable有什么不同? Runnable和Callable都代表那些要在不同的线程中执行的任

java多线程常见面试题

下面是Java线程相关的热门面试题,你可以用它来好好准备面试. 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒.J 2) 线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务.不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间.别把它

JAVA多线程17个问题

Thread.start()方法(native)启动线程,使之进入就绪状态,当cpu分配时间该线程时,由JVM调度执行run()方法. 当你调用start()方法时你将创建新的线程,并且执行在run()方法里的代码.但是如果你直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码. 2.Java中Runnable和Callable有什么不同? Runnable和Callable都代表那些要在不同的线程中执行的任务.Runnable从JDK1.0开始就有了,Callable是在JDK1

Java多线程面试题整理

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒.Java在语言层面对多线程提供了卓越的支持,它也是一个很好的卖点. 2) 线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务.不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间.别把它和栈内存搞混,每个线

java多线程面试题整理及答案(2018年)

java多线程面试题整理及答案(2018年) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对 运算密集型任务提速.比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒.Java在语言层面对多线程提供了卓越的支 持,它也是一个很好的卖点. 线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务.不同的进程使用不同的内存空间,而所

java多线程面试题整理及答案

1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对 运算密集型任务提速.比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒.Java在语言层面对多线程提供了卓越的支 持,它也是一个很好的卖点. 2) 线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务.不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间.别