Java 多线程基础(十)interrupt()和线程终止方式

一、interrupt() 介绍

interrupt() 定义在 Thread 类中,作用是中断本线程

本线程中断自己是被允许的;其它线程调用本线程的 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() 方法,由于固有的不安全性,已经建议不再使用!
下面,我先分别讨论线程在“阻塞状态”和“运行状态”的终止方式,然后再总结出一个通用的方式。

(一)、终止处于“阻塞状态”的线程.

通常,我们通过“中断”方式终止处于“阻塞状态”的线程
当线程由于被调用了 sleep(),,wait(),join() 等方法而进入阻塞状态;若此时调用线程的 interrupt() 将线程的中断标记设为 true。由于处于阻塞状态,中断标记会被清除,同时产生一个InterruptedException 异常。将 InterruptedException 放在适当的位置就能终止线程,形式如下:

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) 循环体之内,就需要额外的添加退出处理。形式如下:

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) 的操作。

(二)、终止处于“运行状态”的线程

通常,我们通过“标记”方式终止处于“运行状态”的线程。其中,包括“中断标记”和“额外添加标记”。

1、通过“中断标记”终止线程

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;
}
public void run() {
while (flag) {
// 执行任务...
}
}

说明:

线程中有一个 flag 标记,它的默认值是 true;并且我们提供 stopTask() 来设置 flag 标记。当我们需要终止该线程时,调用该线程的 stopTask() 方法就可以让线程退出 while 循环。
注意:将 flag 定义为 volatile 类型,是为了保证 flag 的可见性。即其它线程通过 stopTask() 修改了 flag 之后,本线程能看到修改后的 flag 的值。

(三)、通过方式

综合线程处于“阻塞状态”和“运行状态”的终止方式,比较通用的终止线程的形式如下:

public void run() {
try {
// 1. isInterrupted()保证,只要中断标记为true就终止线程。
while (!isInterrupted()) {
// 执行任务...
}
} catch (InterruptedException ie) {
// 2. InterruptedException异常保证,当InterruptedException异常产生时,线程被终止。
}
}
1、isInterrupted()保证,只要中断标记为 true 就终止线程。
2、InterruptedException 异常保证,当 InterruptedException 异常产生时,线程被终止。

三、示例

public class InterruptTest {
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."); Thread.sleep(300);// 休眠300毫秒,然后主线程给t1发“中断”指令,查看t1状态
t1.interrupt();
System.out.println(t1.getName() + "[" + t1.getState() + "] is interrupted."); Thread.sleep(300);// 休眠300毫秒,然后查看t1状态
System.out.println(t1.getName() + "[" + t1.getState() + "] is interrupted now.");
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public void run() {
try {
int i = 0;
while(!isInterrupted()) {
Thread.sleep(100);// 休眠100毫秒
++i;
System.out.println(Thread.currentThread().getName() + "[" + this.getState() + "] loop " + i);
}
}catch(InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "[" + this.getState() + "] catch InterruptedException");
}
}
}
// 运行结果
t1 [ NEW ] is new.
t1 [ RUNNABLE ] is started.
t1 [ RUNNABLE ] loop 1
t1 [ RUNNABLE ] loop 2
t1 [ RUNNABLE ] loop 3
t1 [ RUNNABLE ] catch InterruptedException
t1 [ TERMINATED ] is interrupted.
t1 [ TERMINATED ] is interrupted now.

说明:

①、主线程 main 中通过 new MyThread("t1") 创建线程 t1,之后通过 t1.start() 启动线程 t1。
②、t1 启动之后,会不断的检查它的中断标记,如果中断标记为“false”;则休眠 100ms。
③、t1 休眠之后,会切换到主线程main;主线程再次运行时,会执行t1.interrupt()中断线程t1。t1收到中断指令之后,会将t1的中断标记设置“false”,而且会抛出 InterruptedException 异常。在 t1 的 run() 方法中,是在循环体 while 之外捕获的异常;因此循环被终止。

我们对上面的结果进行小小的修改,将run()方法中捕获InterruptedException异常的代码块移到while循环体内。

public class InterruptTest {
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."); Thread.sleep(300);// 休眠300毫秒,然后主线程给t1发“中断”指令,查看t1状态
t1.interrupt();
System.out.println(t1.getName() + " [ " + t1.getState() + " ] is interrupted."); Thread.sleep(300);// 休眠300毫秒,然后查看t1状态
System.out.println(t1.getName() + " [ " + t1.getState() + " ] is interrupted now.");
}catch(InterruptedException e) {
e.printStackTrace();
} }
}
class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public 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);
}
}
}
// 运行结果
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 [ RUNNABLE ] loop 6
t1 [ RUNNABLE ] is interrupted now.
t1 [ RUNNABLE ] loop 7
...... // 无限循环

说明:

程序进入了死循环了。

这是因为,t1在“等待(阻塞)状态”时,被 interrupt() 中断;此时,会清除中断标记(即 isInterrupted() 会返回 false),而且会抛出 InterruptedException 异常(该异常在while循环体内被捕获)。因此,t1理所当然的会进入死循环了。
解决该问题,需要我们在捕获异常时,额外的进行退出 while 循环的处理。例如,在 MyThread 的 catch(InterruptedException) 中添加 break 或 return 就能解决该问题。

下面是通过“额外添加标记”的方式终止“状态状态”的线程的示例:

public class InterruptTest {
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."); Thread.sleep(300);// 休眠300毫秒,然后主线程给t1发“中断”指令,查看t1状态
t1.stopTask();
System.out.println(t1.getName() + " [ " + t1.getState() + " ] is interrupted."); Thread.sleep(300);// 休眠300毫秒,然后查看t1状态
System.out.println(t1.getName() + " [ " + t1.getState() + " ] is interrupted now.");
}catch(InterruptedException e) {
e.printStackTrace();
} }
}
class MyThread extends Thread{
private volatile boolean flag = true;
public void stopTask() {
flag = false;
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
synchronized (this) {
int i = 0;
while(flag) {
try {
Thread.sleep(100); // 休眠100ms
} catch (InterruptedException ie) {
System.out.println(Thread.currentThread().getName() +" [ "+this.getState()+" ] catch InterruptedException.");
break;
}
i++;
System.out.println(Thread.currentThread().getName()+" [ "+this.getState()+" ] loop " + i);
}
} }
}
// 运行结果
t1 [ NEW ] is new.
t1 [ RUNNABLE ] is started.
t1 [ RUNNABLE ] loop 1
t1 [ RUNNABLE ] loop 2
t1 [ RUNNABLE ] loop 3
t1 [ RUNNABLE ] is interrupted.
t1 [ TERMINATED ] is interrupted now.

四、interrupted() 和 isInterrupted()的区别

interrupted() 和 isInterrupted()都能够用于检测对象的“中断标记”。
区别是,interrupted() 除了返回中断标记之外,它还会清除中断标记(即将中断标记设为 false);而 isInterrupted() 仅仅返回中断标记

Java 多线程基础(十)interrupt()和线程终止方式的更多相关文章

  1. Java 多线程基础(十一)线程优先级和守护线程

    Java 多线程基础(十一)线程优先级和守护线程 一.线程优先级 Java 提供了一个线程调度器来监控程序启动后进去就绪状态的所有线程.线程调度器通过线程的优先级来决定调度哪些线程执行.一般来说,Ja ...

  2. Java多线程基础:进程和线程之由来

    转载: Java多线程基础:进程和线程之由来 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程.当然,Java并发编程涉及到很多方面的内容,不是一朝一夕就能够 ...

  3. 1、Java多线程基础:进程和线程之由来

    Java多线程基础:进程和线程之由来 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程.当然,Java并发编程涉及到很多方面的内容,不是一朝一夕就能够融会贯通 ...

  4. Java 多线程基础(四)线程安全

    Java 多线程基础(四)线程安全 在多线程环境下,如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线 ...

  5. Java 多线程基础(五)线程同步

    Java 多线程基础(五)线程同步 当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题. 要解决上述多线程并发访问一个资源的安全性问题,Java中提供了同步机制 ...

  6. Java 多线程基础(六)线程等待与唤醒

    Java 多线程基础(六)线程等待与唤醒 遇到这样一个场景,当某线程里面的逻辑需要等待异步处理结果返回后才能继续执行.或者说想要把一个异步的操作封装成一个同步的过程.这里就用到了线程等待唤醒机制. 一 ...

  7. Java 多线程基础(七)线程休眠 sleep

    Java 多线程基础(七)线程休眠 sleep 一.线程休眠 sleep sleep() 方法定义在Thread.java中,是 static 修饰的静态方法.sleep() 的作用是让当前线程休眠, ...

  8. Java 多线程基础(八)线程让步

    Java 多线程基础(八)线程让步 yield 一.yield 介绍 yield()的作用是让步.它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权:但是,并 ...

  9. Java多线程系列--“基础篇”09之 interrupt()和线程终止方式

    概要 本章,会对线程的interrupt()中断和终止方式进行介绍.涉及到的内容包括:1. interrupt()说明2. 终止线程的方式2.1 终止处于“阻塞状态”的线程2.2 终止处于“运行状态” ...

随机推荐

  1. word dde payload

    payload: ctrl+F9 {DDEAUTO c:\\windows\\system32\\cmd.exe "/k calc.exe" } Since this techni ...

  2. Window10:不能建立到远程计算机的连接,你可能需要更改此连接的网络设置。

    一,右键我的电脑点击管理. 二,在系统工具中找到设备管理,在设备管理中找到网络适配器. 三,在网络适配器中找到WAN Miniport(IP) 四,找到WAN Miniport(IP)右键放心卸载,作 ...

  3. CentOS 虚拟机 下载及 搭建

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) CentOS 虚拟机安装包下载 : 链接:https://pan.baidu.com/s/1JDIASm ...

  4. 用python实现汉诺塔问题

    一.用动画实现汉诺塔问题: import turtle class Stack: def __init__(self): self.items = [] def isEmpty(self): retu ...

  5. OkHttp,一次无奈的使用

    一次使用OKHTTP的心痛历程 最近由于一些不得已的原因,接触到了OKHttp,说起来也挺Dan疼的,之前同事将生产附件上传地址配置成了测试地址,还好数量不多,没有造成太大的影响,况且的是这位同事又离 ...

  6. Java实现 蓝桥杯 算法提高 矩阵乘法(暴力)

    试题 算法提高 矩阵乘法 问题描述 小明最近刚刚学习了矩阵乘法,但是他计算的速度太慢,于是他希望你能帮他写一个矩阵乘法的运算器. 输入格式 输入的第一行包含三个正整数N,M,K,表示一个NM的矩阵乘以 ...

  7. Java实现 蓝桥杯VIP 算法训练 字符串逆序

    问题描述 给定一个字符串,将这个串的所有字母逆序后输出. 输入格式 输入包含一个字符串,长度不超过100,字符串中不含空格. 输出格式 输出包含一个字符串,为上面字符串的逆序. 样例输入 tsinse ...

  8. Java实现 蓝桥杯VIP 算法提高 我们的征途是星辰大海

    算法提高 我们的征途是星辰大海 时间限制:1.0s 内存限制:256.0MB 最新的火星探测机器人curiosity被困在了一个二维迷宫里,迷宫由一个个方格组成. 共有四种方格: '.' 代表空地,c ...

  9. Java实现 蓝桥杯VIP 算法提高 三角形面积

    算法提高 三角形面积 时间限制:1.0s 内存限制:256.0MB 问题描述 由三角形的三边长,求其面积. 提示:由三角形的三边a,b,c求面积可以用如下的公式: s=(a+b+c)/2 输入格式 由 ...

  10. Java实现复数运算

    1 问题描述 编程实现两个复数的运算.设有两个复数 和 ,则他们的运算公式为: 要求:(1)定义一个结构体类型来描述复数. (2)复数之间的加法.减法.乘法和除法分别用不用的函数来实现. (3)必须使 ...