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

简介:

一,介绍

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

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

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

 

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

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

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

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

复制代码
 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(20);//modify 2000 to 20
 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线程睡眠20ms后,执行第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行语句不会执行)。这里表明: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异常的方式,如下:

复制代码
 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 }
复制代码

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

 本文转自hapjin博客园博客,原文链接:http://www.cnblogs.com/hapjin/p/5450779.html,如需转载请自行联系原作者

相关文章
|
1天前
|
安全 Java 调度
Java线程:深入理解与实战应用
Java线程:深入理解与实战应用
14 0
|
2天前
|
Java
Java中的并发编程:理解和应用线程池
【4月更文挑战第23天】在现代的Java应用程序中,性能和资源的有效利用已经成为了一个重要的考量因素。并发编程是提高应用程序性能的关键手段之一,而线程池则是实现高效并发的重要工具。本文将深入探讨Java中的线程池,包括其基本原理、优势、以及如何在实际开发中有效地使用线程池。我们将通过实例和代码片段,帮助读者理解线程池的概念,并学习如何在Java应用中合理地使用线程池。
|
6天前
|
安全 Java
深入理解 Java 多线程和并发工具类
【4月更文挑战第19天】本文探讨了Java多线程和并发工具类在实现高性能应用程序中的关键作用。通过继承`Thread`或实现`Runnable`创建线程,利用`Executors`管理线程池,以及使用`Semaphore`、`CountDownLatch`和`CyclicBarrier`进行线程同步。保证线程安全、实现线程协作和性能调优(如设置线程池大小、避免不必要同步)是重要环节。理解并恰当运用这些工具能提升程序效率和可靠性。
|
6天前
|
安全 Java
java多线程(一)(火车售票)
java多线程(一)(火车售票)
|
7天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
7天前
|
安全 Java 程序员
Java中的多线程并发编程实践
【4月更文挑战第18天】在现代软件开发中,为了提高程序性能和响应速度,经常需要利用多线程技术来实现并发执行。本文将深入探讨Java语言中的多线程机制,包括线程的创建、启动、同步以及线程池的使用等关键技术点。我们将通过具体代码实例,分析多线程编程的优势与挑战,并提出一系列优化策略来确保多线程环境下的程序稳定性和性能。
|
7天前
|
缓存 分布式计算 监控
Java并发编程:深入理解线程池
【4月更文挑战第17天】在Java并发编程中,线程池是一种非常重要的技术,它可以有效地管理和控制线程的执行,提高系统的性能和稳定性。本文将深入探讨Java线程池的工作原理,使用方法以及在实际开发中的应用场景,帮助读者更好地理解和使用Java线程池。
|
15天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
26天前
|
存储 缓存 NoSQL
Redis单线程已经很快了6.0引入多线程
Redis单线程已经很快了6.0引入多线程
31 3
|
29天前
|
消息中间件 安全 Linux
线程同步与IPC:单进程多线程环境下的选择与权衡
线程同步与IPC:单进程多线程环境下的选择与权衡
58 0