Java多线程(九)—— interrupt()和线程终止方式
一、interrupt() 说明
interrupt()的作用是中断本线程。
本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。
如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。
如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。
如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。
中断一个“已终止的线程”不会产生任何操作。
二、终止线程的方式
Thread中的stop()和suspend()方法,由于固有的不安全性,已经建议不再使用!
下面,我先分别讨论线程在“阻塞状态”和“运行状态”的终止方式,然后再总结出一个通用的方式。
1、终止处于“阻塞状态”的线程
通常,我们通过“中断”方式终止处于“阻塞状态”的线程。
当线程由于被调用了sleep(), wait(), join()等方法而进入阻塞状态;若此时调用线程的interrupt()将线程的中断标记设为true。由于处于阻塞状态,中断标记会被清除,同时产生一个InterruptedException异常。将InterruptedException放在适当的位置就能终止线程,形式如下:
@Override public void run() {try {while (true) {// 执行任务... }} catch (InterruptedException ie) { // 由于产生InterruptedException异常,退出while(true)循环,线程终止! } }
说明:在while(true)中不断的执行任务,当线程处于阻塞状态时,调用线程的interrupt()产生InterruptedException中断。中断的捕获在while(true)之外,这样就退出了while(true)循环!
注意:对InterruptedException的捕获务一般放在while(true)循环体的外面,这样,在产生异常时就退出了while(true)循环。否则,InterruptedException在while(true)循环体之内,就需要额外的添加退出处理。形式如下:
@Override public void run() {while (true) {try {// 执行任务...} catch (InterruptedException ie) { // InterruptedException在while(true)循环体内。// 当线程产生了InterruptedException异常时,while(true)仍能继续运行!需要手动退出break;}} }
说明:上面的InterruptedException异常的捕获在whle(true)之内。当产生InterruptedException异常时,被catch处理之外,仍然在while(true)循环体内;要退出while(true)循环体,需要额外的执行退出while(true)的操作。
2、终止处于“运行状态”的线程
通常,我们通过“标记”方式终止处于“运行状态”的线程。其中,包括“中断标记”和“额外添加标记”。
(1)通过“中断标记”终止线程
形式如下:
@Override public void run() {while (!isInterrupted()) {// 执行任务... } }
说明:isInterrupted()是判断线程的中断标记是不是为true。当线程处于运行状态,并且我们需要终止它时,可以调用线程的interrupt()方法,使用线程的中断标记为true,即isInterrupted()会返回true。此时,就会退出while循环。
注意:interrupt()并不会终止处于“运行状态”的线程!它会将线程的中断标记设为true。
(2)通过“额外添加标记”。
形式如下:
private volatile boolean flag= true; protected void stopTask() {flag = false; }@Override public void run() {while (flag) {// 执行任务... } }
说明:线程中有一个flag标记,它的默认值是true;并且我们提供stopTask()来设置flag标记。当我们需要终止该线程时,调用该线程的stopTask()方法就可以让线程退出while循环。
注意:将flag定义为volatile类型,是为了保证flag的可见性。即其它线程通过stopTask()修改了flag之后,本线程能看到修改后的flag的值。
综合线程处于“阻塞状态”和“运行状态”的终止方式,比较通用的终止线程的形式如下:
@Override public void run() {try {// 1. isInterrupted()保证,只要中断标记为true就终止线程。while (!isInterrupted()) {// 执行任务... }} catch (InterruptedException ie) { // 2. InterruptedException异常保证,当InterruptedException异常产生时,线程被终止。 } }
三、终止线程的示例
interrupt()常常被用来终止“阻塞状态”线程。参考下面示例:
package com.demo.interrupt;public class MyThread extends Thread{public MyThread(String name) {super(name);}@Overridepublic void run() {try { int i=0;while (!isInterrupted()) {Thread.sleep(100); // 休眠100msi++;System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i); }} catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException."); }} }
package com.demo.interrupt;public class Demo1 {public static void main(String[] args) { try { Thread t1 = new MyThread("t1"); // 新建“线程t1”System.out.println(t1.getName() +" ("+t1.getState()+") is new."); t1.start(); // 启动“线程t1”System.out.println(t1.getName() +" ("+t1.getState()+") is started."); // 主线程休眠300ms,然后主线程给t1发“中断”指令。Thread.sleep(300);t1.interrupt();System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");// 主线程休眠300ms,然后查看t1的状态。Thread.sleep(300);System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");} catch (InterruptedException e) { e.printStackTrace();}} }
运行结果:
t1 (NEW) is new. t1 (RUNNABLE) is started. t1 (RUNNABLE) loop 1 t1 (RUNNABLE) loop 2 t1 (TIMED_WAITING) is interrupted. t1 (RUNNABLE) catch InterruptedException. t1 (TERMINATED) is interrupted now.
结果说明:
(01) 主线程main中通过new MyThread("t1")创建线程t1,之后通过t1.start()启动线程t1。
(02) t1启动之后,会不断的检查它的中断标记,如果中断标记为“false”,则休眠100ms。
(03) t1休眠之后,会切换到主线程main;主线程再次运行时,会执行t1.interrupt()中断线程t1。t1收到中断指令之后,会将t1的中断标记设置“false”,而且会抛出InterruptedException异常。在t1的run()方法中,是在循环体while之外捕获的异常;因此循环被终止。
我们对上面的结果进行小小的修改,将run()方法中捕获InterruptedException异常的代码块移到while循环体内。
package com.demo.interrupt;public class MyThread extends Thread{public MyThread(String name) {super(name);}@Overridepublic void run() {int i=0;while (!isInterrupted()) {try {Thread.sleep(100); // 休眠100ms} catch (InterruptedException ie) { System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException."); }i++;System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i); }}}
package com.demo.interrupt;public class Demo2 {public static void main(String[] args) { try { Thread t1 = new MyThread("t1"); // 新建“线程t1”System.out.println(t1.getName() +" ("+t1.getState()+") is new."); t1.start();// 启动“线程t1”System.out.println(t1.getName() +" ("+t1.getState()+") is started."); // 主线程休眠300ms,然后主线程给t1发“中断”指令。Thread.sleep(300);t1.interrupt();System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");// 主线程休眠300ms,然后查看t1的状态。Thread.sleep(300);System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");} catch (InterruptedException e) { e.printStackTrace();}} }
运行结果:
t1 (NEW) is new. t1 (RUNNABLE) is started. t1 (RUNNABLE) loop 1 t1 (RUNNABLE) loop 2 t1 (TIMED_WAITING) is interrupted. t1 (RUNNABLE) catch InterruptedException. t1 (RUNNABLE) loop 3 t1 (RUNNABLE) loop 4 t1 (RUNNABLE) loop 5 t1 (TIMED_WAITING) is interrupted now. t1 (RUNNABLE) loop 6 t1 (RUNNABLE) loop 7 t1 (RUNNABLE) loop 8 t1 (RUNNABLE) loop 9 ....
结果说明:
程序进入了死循环!
为什么会这样呢?这是因为,t1在“等待(阻塞)状态”时,被interrupt()中断;此时,会清除中断标记[即isInterrupted()会返回false],而且会抛出InterruptedException异常[该异常在while循环体内被捕获]。因此,t1理所当然的会进入死循环了。
解决该问题,需要我们在捕获异常时,额外的进行退出while循环的处理。例如,在MyThread的catch(InterruptedException)中添加break 或 return就能解决该问题。
下面是通过“额外添加标记”的方式终止“状态状态”的线程的示例:
package com.demo.interrupt;public class MyThread extends Thread {private volatile boolean flag= true;public void stopTask() {flag = false;}public MyThread(String name) {super(name);}@Overridepublic void run() {synchronized(this) {try {int i=0;while (flag) {Thread.sleep(100); // 休眠100msi++;System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i); }} catch (InterruptedException ie) { System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException."); }} } }
package com.demo.interrupt;public class Demo3 {public static void main(String[] args) { try { MyThread t1 = new MyThread("t1"); // 新建“线程t1”System.out.println(t1.getName() +" ("+t1.getState()+") is new."); t1.start(); // 启动“线程t1”System.out.println(t1.getName() +" ("+t1.getState()+") is started."); // 主线程休眠300ms,然后主线程给t1发“中断”指令。Thread.sleep(300);t1.stopTask();System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");// 主线程休眠300ms,然后查看t1的状态。Thread.sleep(300);System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");} catch (InterruptedException e) { e.printStackTrace();}} }
运行结果:
t1 (NEW) is new. t1 (RUNNABLE) is started. t1 (RUNNABLE) loop 1 t1 (RUNNABLE) loop 2 t1 (RUNNABLE) is interrupted. t1 (RUNNABLE) loop 3 t1 (TERMINATED) is interrupted now.
四、interrupt() 和 isInterrupted()的区别
最后谈谈 interrupt() 和 isInterrupted()。interrupt() 和 isInterrupted()都能够用于检测对象的“中断标记”。区别是,interrupt()除了返回中断标记之外,它还会清除中断标记(即将中断标记设为false);而isInterrupted()仅仅返回中断标记。
Java多线程(九)—— interrupt()和线程终止方式相关推荐
- Java多线程系列--“基础篇”09之 interrupt()和线程终止方式
概要 本章,会对线程的interrupt()中断和终止方式进行介绍.涉及到的内容包括: 1. interrupt()说明 2. 终止线程的方式 2.1 终止处于"阻塞状态"的线 ...
- interupt java_Java多线程系列--“基础篇”09之 interrupt()和线程终止方式
概要 1. interrupt()说明 在介绍终止线程的方式之前,有必要先对interrupt()进行了解. Interrupts this thread. Unless the current th ...
- Java多线程详解(线程不安全案例)
嗨喽-小伙伴们我又来了, 通过前面两章的学习,我们了解了线程的基本概念和创建线程的四种方式. 附上链接: 1. Java多线程详解(基本概念) 2. Java多线程详解(如何创建线程) ...
- 深入多线程九:守护线程(代码示例)
在学习Java的道路上,是否路过多线程时总让你很迷惘:很不巧,我也是,而使我们感到很迷惘主要原因都源于没有对概念的深深的理解和实践.所以我决定漫步Java多线程,同你一起会会多线程. 深入多线程系列 ...
- Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字
线程安全问题是多线程编程中最典型的一类问题之一.如果多线程环境下代码运行的结果是符合我们预期的,即该结果正是在单线程环境中应该出现的结果,则说这个程序是线程安全的. 通俗来说,线程不安全指的就是某一代 ...
- Java 多线程(七) 线程间的通信
Java 多线程(七) 线程间的通信--wait及notify方法 线程间的相互作用 线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务. Object类中相关的方法有两个notify方法 ...
- Java 多线程(三) 线程的生命周期及优先级
Java 多线程(三) 线程的生命周期及优先级 线程的生命周期 线程的生命周期:一个线程从创建到消亡的过程. 如下图,表示线程生命周期中的各个状态: 线程的生命周期可以分为四个状态: 1.创建状态: ...
- Java多线程系列(五):线程池的实现原理、优点与风险、以及四种线程池实现
为什么需要线程池 我们有两种常见的创建线程的方法,一种是继承Thread类,一种是实现Runnable的接口,Thread类其实也是实现了Runnable接口.但是我们创建这两种线程在运行结束后都会被 ...
- Java 多线程(八) 线程状态图
Java 多线程(八) 线程状态图 结合多线程的学习过程,介绍线程的状态图,随着学习的深入,这幅图不断加入新的内容. 一.线程基本状态图 这幅图是在Java 多线程(三) 线程的生命周期及优先级出现过 ...
最新文章
- 混合式APP开发中中间件方案Rexsee
- AI时代将临,各国战略及企业布局有何特点?
- How good software makes us stupid?
- Scikit中的特征选择,XGboost进行回归预测,模型优化的实战
- Python中安装模块的方法
- 1.3 xss原理分析与剖析(4)
- 【STM32】USART相关函数和类型
- matlab fminimax 例子,Matlab应用实例(8)—fminimax
- linux中dns超时时间,Linux DNS timeout, attempts.---DNS超时,重试的配置
- php mysql搜索算法_PHP实现深度优先搜索算法(DFS,Depth First Search)详解
- java获取access token_微信java 开发4 access_token获取
- cad渐开线齿轮轮廓绘制_CAD画齿轮的渐开线程序 (lsp)和渐开线齿轮关系
- 难于传播的正能量,来点干货
- 安卓原生开机动画_安卓开机动画 74款
- 聚类算法之DBSCAN
- neural-style风格迁移模型实战
- 周董演唱会为什么总是抢不到票?教你用Python做一个自动抢票脚本
- Rxjava2中Single的just操作符源码学习
- 2.3.4nbsp;《孙子兵法》的基本原则
- 3DMAx:能导入导出的文件格式
热门文章
- SAP MM MIRO的时候,发票价格与物料主数据的价格(移动平均价)有差异的处理
- python每隔2s执行一次hello world!
- Pytorch固定随机性
- Pytorch中改变形状和交换维度详解:view()、reshape()、transpose()、permute()以及contiguous()
- 最简单的t-SNE上手使用
- 什么是Attention机制以及Pytorch如何使用
- 这些行业,将率先落地AI芯片 | 星前沿
- Cell Reports:黄梓芮等揭示大脑皮层中通往意识的“大门”
- 梅宏院士:操作系统变迁有20年周期律,泛在计算是一片新蓝海
- 逐!帧!揭!秘!终于能看清波士顿动力机器人的细节了