Thread.interrupt() 使用不当,导致程序无法退出

简介:

http://blog.chenlb.com/2009/07/incorrect-use-thread-interrupt-cause-not-exit.html


Java Thread.interrupt() 使用不当,导致多线程程序无法正常退出。前段时间写的一个多线程程序:一个子线程基本是死循环地从任务池里取出任务(取的时候,没有任务会阻塞),并运行可用的任务。没有任务了,完成的时候 main 线程调用子线程的中断,抓到中断后退出子线程的死循环。

上面的程序已经正常的运行了几个月了,是一天运行一次而已。近期要在每4分钟运行一次的应用中使用了这程序。运行一天多就会阻塞掉了(一直不会退出),今天是第二次了。观察分析日志:是在 main 调用 taskThread.interrupt() 会没有输出日志(实际就是没有中断到子线程),而死循环是在接受到中断才会把循环条件设为 false。所以程序 block了,不退出了。

分析:调用 taskThread.interrupt() 为什么没有接受到中断(实际是 InterruptedException)呢?找原理:Thread.interrupt() 的调用对正在运行的线程是不起作用的,只有对阻塞的线程有效。下面是引用:Java Thread.interrupt 害人! 中断JAVA线程(zz) 的一段重要的话。

使用Thread.interrupt()中断线程

Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。

因此,如果线程被上述几种方法阻塞,正确的停止线程方式是设置共享变量,并调用interrupt()(注意变量应该先设置)。如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。在任何一种情况中,最后线程都将检查共享变量然后再停止。

程序代码块大概:

public void run() {  
    while(running) {  
        Object obj = null;  
        try {  
            //...  
            //取出数据  
            obj = taskQueue.take();  
            //执行任务...  
        } catch (InterruptedException e) {  
            running = false;  
            log.info("被 interrupt, 退出");  
        }  
  
    }   //while  
    //....  
}  

描述现在的程序:使子线程阻塞的地方是从任务池取数据,具体来说是 java.util.concurrent.LinkedBlockingQueue.take() 方法。它在 try/catch 内,catch 到 InterruptedException 设置执行任务的循环条件变量。然后就退出 run。我草率地认为interrupt被调用了(认为take会被阻塞,有一段每200ms检测全部任务是否完成),一定能 catch 到中断。也没有在调用 taskThread.interrupt() 之前把循环条件变量设为 false。所以造成有时候子线程不能退出。

当执行最后一个任务还没有完成时,main 调用 taskThread.interrupt() 就会失效,因为子线程没有 blocking。

解决办法是在调用 interrupt 之前,running = false。


相关文章
|
Oracle Java 关系型数据库
Oracle jdk 的国内下载镜像
Oracle jdk 的国内下载镜像
52470 0
|
Java API
JDK API文档中文版(1.6、1.8、1.9)(附百度网盘下载地址)
JDK API文档中文版(1.6、1.8、1.9)(附百度网盘下载地址)
6739 3
JDK API文档中文版(1.6、1.8、1.9)(附百度网盘下载地址)
|
安全 数据安全/隐私保护 块存储
cephx: ceph的认证和加密协议
Ceph作为一个分布式存储系统,支持对象存储、块设备和文件系统。为了在网络传输中防止数据被篡改,做到较高程度的安全性,加入了Cephx加密认证协议。其目的是识别身份,加密、验证传输中的数据。 在ceph系统中,元数据保存在一个叫做ceph-mon的进程中,也可以称为monitor节点,系统可以有多个monitor副本节点,用paxos保持数据一致性。 这里不谈paxos,也不谈多个monitor
5369 0
|
缓存 Linux Apache
网络IO和磁盘IO详解
1. 缓存IO          缓存I/O又被称作标准I/O,大多数文件系统的默认I/O操作都是缓存I/O。在Linux的缓存I/O机制中,数据先从磁盘复制到内核空间的缓冲区,然后从内核空间缓冲区复制到应用程序的地址空间。
5109 0
|
SQL 存储 开发框架
数据库必知词汇:用户定义函数(UDF)
用户定义函数(UDF) 由一个或多个SQL语句组成的子程序,可用于封装代码以便重新使用。通常情况下不将用户限制在定义为SQL语言一部分的内置函数上,而是允许用户创建自己的用户定义函数。
2594 0
|
编译器 C语言 计算机视觉
C语言实现的图像处理程序
C语言实现的图像处理程序
|
存储 算法 安全
数据仓库与数据挖掘概述
数据仓库与数据挖掘概述
574 3
|
Java Maven Spring
Maven 依赖搜索顺序
Maven在构建时按顺序搜索依赖:先本地仓库,再中央仓库,如有远程仓库则尝试搜索,未找到会报错。若需加速,可改用阿里云仓库。在`settings.xml`的`mirrors`节点添加阿里云镜像如`aliyunmaven`,并在`pom.xml`的`dependencies`中指定所需依赖。使用`mvn install`拉取。
|
人工智能 自然语言处理
一站式视频生成-MotionAgent操作指南
这是一段特效短片,虽然只有四秒,但它在推特上斩获了十多万浏览。
|
SQL 消息中间件 存储
尘锋信息基于 Apache Paimon 的流批一体湖仓实践
尘锋信息基于 Apache Paimon 构建流批一体湖仓
13485 1
尘锋信息基于 Apache Paimon 的流批一体湖仓实践