【杂谈】线程中断——Interrupt
前言
以前有一个错误的认识,以为中断操作都会抛出异常,后来才发现并不是这样,所以今天就来做一个关于中断的总结。
如何关闭线程
已被弃用的Stop方法
早期,Thread类中有一个stop方法,用于强行关闭一个线程。但是后来发现此操作并不安全,强行关闭可能导致一致性问题。故stop方法已被官方弃用。具体原因请看Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?.。
既然,stop方法不能用了,我们就要另辟蹊径。而我们知道,只要线程的run方法"完成",底层操作系统的线程就会被释放。"完成"意味着,run方法结束,而结束的方式有两种,一种是抛出异常,另一种则是方法返回。所以,我们要做的就是控制线程任务,抛出异常或返回(return)。
Flag机制
前面说到对线程任务的执行进行控制,而线程任务一旦跑起来,又如何对其执行情况进行干预呢?答案就是,让代码反复检查某个值的状态,如果达到某个状态,就结束执行(return)或者抛出异常。而这个值,可以被外部访问到,用户可以在需要的时候设定"结束Flag"。
例如:
class Task implements Runnable {
private volatile boolean stop = false; public void stop() {
stop = true;
} public void run() {
while(!stop) { //每执行一次操作,检查一次状态
System.out.println("I'm running...");
}
System.out.println("I'm stopped.");
}
} public class Main {
public void main(String[] args) throws InterruptedException{
Task task = new Task();
Thread t = new Thread(task);
t.start(); //启动任务线程
Thread.sleep(3*1000); //主线程休眠3秒
task.stop();//三秒后关闭任务线程
}
}
中断机制
强制结束线程被取消,取而代之的,是一种协作机制,即中断机制。也就是说,一个线程只能向另一个线程发送中断信号,而不能强行对其进行关闭。而另一个线程如何处理,就得看其正在执行的代码对中断信号如何反应了。其实就是前面说的Flag机制,但是,其内部维护Flag,还有额外的和阻塞库交互的内容。例子如下:
class Task implements Runnable {
public void run() {
while(!Thread.currentThread().interrupted()){ //检查线程中断状态
System.out.println("I'm running...");
}
System.out.println("I'm stopped.");
}
} public class Main {
public void main(String[] args) throws InterruptedException{
Task task = new Task();
Thread t = new Thread(task);
t.start(); //启动任务线程
Thread.sleep(3*1000); //主线程休眠3秒
t.interrupt();//三秒后中断任务线程
}
}
其中,Thread.currentThread()静态方法将获得当前执行此段代码的线程对象。然后调用interrupted()方法获取线程中断状态,如果线程被中断,则返回true。线程的中断状态可以用interrupt()方法设置。
下面提一下中断可能产生让人产生疑惑的几个方法:
- interrupt() => 设置中断状态,设置为已中断
- isInterrupted() => 获取中断状态
- interrupted() => 恢复中断状态,并返回恢复前的状态。(即如果被中断,会设置为未中断,并返回true)
interrupt方法到底做了什么
要想查明原因,最好的方法就是查看源码。我们先来看看interrupt代码
其中,同步块可以暂时无视,因为那是跟I/O操作挂钩的I/O中断器,在进行I/O操作是,对应类库会对其进行设置。那么剩下的就只有一个方法interrupt0()。显然这是一个本地方法。那我们就看看JVM中,这个方法的实现。先来看看与interrupt0关联的方法是什么。在线查看链接(墙外)
由上述关联方法可知,本地方法的实现依赖于JVM实现。下面内容以Hospot虚拟机为例。以下来自hotspot源码\src\share\vm\prims\jvm.cpp
前面只是做一些状态检查,最主要的是调用Thread::interrupt函数。以下来自hospot源码\src\share\vm\runtime\thread.cpp
此处调用os,即操作系统的中断方法。以下来自hospot源码\src\os\linux\vm\os_linux.cpp
以上主要进行了两个操作,一个设置中断状态,一个是唤醒当前线程(如果当前线程处于挂起状态)。
为何需要唤醒线程?
前面已经说了,中断跟Flag机制差不多,不会实际上关闭线程。要想关闭线程,必须让线程方法返回或者抛出异常。如果不唤醒线程,则代码也就不会被执行。任务会一直处于无法完成的状态。
Wait()、Sleep()与中断异常
如果wait或sleep方法被中断,会抛出中断异常。这就是前面说的与阻塞库的交互内容。但是我们已经看到,中断操作实际上只是设置了中断状态,和唤醒线程。那么异常抛出又是在哪里实现的呢?而且wait和sleep方法同样是本地方法,那没办法了,只能再看源码,以下以sleep方法为例。以下来自hotspot源码\src\share\vm\prims\jvm.cpp
再来看看os::sleep里面是如何处理的。以下来自hotspot源码\src\os\linux\vm\os_linux.cpp
由以上代码可知,sleep函数会将线程挂起,然后代码卡死在挂起操作上,然后其他线程中断了此线程,此线程被唤醒,然后sleep函数继续执行,检查中断状态,如果已中断,则抛出中断异常。
为何要恢复中断状态
这就跟线程的复用有关了。首先要明确一点的是,打断线程任务和打断线程是两码事。我们可以通过中断打断线程任务,在线程池的案例中,此任务结束后,线程会继续获取并执行其他任务,并没有因为任务的中断而关闭线程。也就是说,当前线程完成了当前的任务,会继续完成其他任务。那这时候如果线程还保留着中断状态,就会对后续的任务执行有影响。影响何在呢?看看上面的代码,有没有注意到,sleep函数在挂起线程之前会先检查线程的中断状态。如果线程处于中断状态,则直接抛出中断异常。wait函数也是同理。那这样的话,其他任务就无法挂起或休眠此线程了。因此,我们在检查中断状态的时候,还需将其中断状态恢复,即执行interrupted()方法即可。
【杂谈】线程中断——Interrupt的更多相关文章
- 线程中断 interrupt 和 LockSupport
本文章将要介绍的内容有以下几点,读者朋友也可先自行思考一下相关问题: 线程中断 interrupt 方法怎么理解,意思就是线程中断了吗?那当前线程还能继续执行吗? 判断线程是否中断的方法有几个,它们之 ...
- 日积月累--线程中断interrupt()方法
线程中断方法interrupt()方法的理解: interrupt()方法的源码: interrupted()方法的源码及注解: isInterrupted()方法源码及注解: 在了解这个方法之前我们 ...
- Java 并发:线程中断-interrupt
一直以为执行了interrupt方法就可以让线程结束,并抛出InterruptedException. 今天看了Java并发编程实战的第七章发现并不是这么回事,在这章的开头就提到 要使任务和线程能安全 ...
- java线程中断[interrupt()函数] (转载)
一个正常的线程中断: 从运行到真正的结束,应该有三个阶段: 正常运行. 处理结束前的工作,也就是准备结束. 结束退出. Java曾经提供过抢占式限制中断,但问题多多,例如的Thread.stop.另一 ...
- java之线程中断——interrupt
如下图所示,interrupt()方法并没有成功的中断我们的线程. 为了便于理解,其实可以这样来类比(注意,只是类比,实际情况并不完全是这样):Thread类中有一个boolean的标志域用来表示线程 ...
- 线程的中断.interrupt
线程对象.interrupt() 注意,异常分析中要有break,否则无法中断 public class Demo extends JFrame { private Thread thread;//定 ...
- 线程中断方法interrupt() 与 cancel()
(一).关于interrupt() interrupt()并不直接中断线程,而是设定一个中断标识,然后由程序进行中断检查,确定是否中断. 1. sleep() & interr ...
- 从头认识java-17.2 线程中断(interrupt)
这一章节我们来讨论一下线程中断(interrupt). 1.什么是线程中断(interrupt)? 就是在多线程执行的时候,我们给线程贴上一个中断的标记.可是不要求线程终止. 2.样例: 中断的样例: ...
- 线程中断:Thread类中interrupt()、interrupted()和 isInterrupted()方法详解
首先看看官方说明: interrupt()方法 其作用是中断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程),但实际上只是给线程设置一个中断标志,线程仍会继续运行. i ...
随机推荐
- POI2014解题报告
穷酸博主没有bz权限号, 也不会去$poi$官网, 在某咕刷的$poi$,按照某咕的难度排序刷, poi~ $Luogu3572 PTA-Little Bird$ 单调队列, 队列内按照 步数为第一关 ...
- 查找 管道 exec
#查找150天为使用的文件并列出find -type f -mtime +150 -exec ls -ltr {} \;#查找150天内120外的文件find -type f -mtime -150 ...
- django学习install apps注册错了的影响
今天在学习例子的时候 不注意吧settings.py里面的INSTALL APPS 的APP应用名称写错了 应该是blog 写成了myblog 结果导致python manage.py makemi ...
- DOM追加笔记
根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点: 整个文档是一个文档节点 每个 HTML 元素是元素节点 HTML 元素内的文本是文本节点 每个 HTML 属性是属性节点 ...
- 学习Acegi应用到实际项目中(5)
实际企业应用中,用户密码一般都会进行加密处理,这样才能使企业应用更加安全.既然密码的加密如此之重要,那么Acegi(Spring Security)作为成熟的安全框架,当然也我们提供了相应的处理方式. ...
- ABP框架系列之四十五:(Quartz-Integration-Quartz-集成)
Introduction Quartz is a is a full-featured, open source job scheduling system that can be used from ...
- android启动画面隐藏状态栏全屏显示
1.在根部局给一个id,然后直接设置就行了layout.setSystemUiVisibility(View.INVISIBLE); 状态栏就没有了. 2.如果你只是想改变状态栏颜色的也可以 //5. ...
- Error:(18, 51) java: -source 1.5 中不支持 diamond 运算符 (请使用 -source 7 或更高版本以启用 diamond 运算符)
问题:主要是因为jdk版本不一样 解决: 方法一:List<String> list=new ArrayList<Stirng>(); 方法二:重新安装jdk8的版本(安装和配 ...
- 【leetcode】 算法题2 两数相加
问题 给定两个非空链表来表示两个非负整数.位数按照逆序方式存储,它们的每个节点只存储单个数字.将两数相加返回一个新的链表. 你可以假设除了数字 0 之外,这两个数字都不会以零开头. 示例 ...
- ASP.NET Core OceLot 微服务实践
1.OceLot中间件介绍 在传统的BS应用中,随着业务需求的快速发展变化,需求不断增长,迫切需要一种更加快速高效的软件交付方式.微服务可以弥补单体应用不足,是一种更加快速高效软件架构风格.单体应用被 ...