文档章节

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

shanxi_liyong
 shanxi_liyong
发布于 2017/01/25 13:04
字数 2070
阅读 9
收藏 1

1、终止线程的方式

Thread中的stop()和suspend()方法,由于固有的不安全性,已经建议不再使用!
下面,我先分别讨论线程在“阻塞状态”和“运行状态”的终止方式,然后再总结出一个通用的方式。

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

1.2 终止处于“运行状态”的线程

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

@Override
public void run() {
    while (!isInterrupted()) {
        // 执行任务...
    }
}

说明:isInterrupted()是判断线程的中断标记是不是为true。当线程处于运行状态,并且我们需要终止它时;可以调用线程的interrupt()方法,使用线程的中断标记为true,即isInterrupted()会返回true。此时,就会退出while循环。
注意:interrupt()并不会终止处于“运行状态”的线程!它会将线程的中断标记设为true。

(02) 通过“额外添加标记”。
形式如下:

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异常产生时,线程被终止。
    }
}

2. 终止线程的示例

interrupt()常常被用来终止“阻塞状态”线程。参考下面示例:

1 // Demo1.java的源码
 2 class MyThread extends Thread {
 3     
 4     public MyThread(String name) {
 5         super(name);
 6     }
 7 
 8     @Override
 9     public void run() {
10         try {  
11             int i=0;
12             while (!isInterrupted()) {
13                 Thread.sleep(100); // 休眠100ms
14                 i++;
15                 System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
16             }
17         } catch (InterruptedException e) {  
18             System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");  
19         }
20     }
21 }
22 
23 public class Demo1 {
24 
25     public static void main(String[] args) {  
26         try {  
27             Thread t1 = new MyThread("t1");  // 新建“线程t1”
28             System.out.println(t1.getName() +" ("+t1.getState()+") is new.");  
29 
30             t1.start();                      // 启动“线程t1”
31             System.out.println(t1.getName() +" ("+t1.getState()+") is started.");  
32 
33             // 主线程休眠300ms,然后主线程给t1发“中断”指令。
34             Thread.sleep(300);
35             t1.interrupt();
36             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
37 
38             // 主线程休眠300ms,然后查看t1的状态。
39             Thread.sleep(300);
40             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
41         } catch (InterruptedException e) {  
42             e.printStackTrace();
43         }
44     } 
45 }

运行结果

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循环体内。

1 // Demo2.java的源码
 2 class MyThread extends Thread {
 3     
 4     public MyThread(String name) {
 5         super(name);
 6     }
 7 
 8     @Override
 9     public void run() {
10         int i=0;
11         while (!isInterrupted()) {
12             try {
13                 Thread.sleep(100); // 休眠100ms
14             } catch (InterruptedException ie) {  
15                 System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");  
16             }
17             i++;
18             System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
19         }
20     }
21 }
22 
23 public class Demo2 {
24 
25     public static void main(String[] args) {  
26         try {  
27             Thread t1 = new MyThread("t1");  // 新建“线程t1”
28             System.out.println(t1.getName() +" ("+t1.getState()+") is new.");  
29 
30             t1.start();                      // 启动“线程t1”
31             System.out.println(t1.getName() +" ("+t1.getState()+") is started.");  
32 
33             // 主线程休眠300ms,然后主线程给t1发“中断”指令。
34             Thread.sleep(300);
35             t1.interrupt();
36             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
37 
38             // 主线程休眠300ms,然后查看t1的状态。
39             Thread.sleep(300);
40             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
41         } catch (InterruptedException e) {  
42             e.printStackTrace();
43         }
44     } 
45 }

运行结果

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就能解决该问题。

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

1 // Demo3.java的源码
 2 class MyThread extends Thread {
 3 
 4     private volatile boolean flag= true;
 5     public void stopTask() {
 6         flag = false;
 7     }
 8     
 9     public MyThread(String name) {
10         super(name);
11     }
12 
13     @Override
14     public void run() {
15         synchronized(this) {
16             try {
17                 int i=0;
18                 while (flag) {
19                     Thread.sleep(100); // 休眠100ms
20                     i++;
21                     System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
22                 }
23             } catch (InterruptedException ie) {  
24                 System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");  
25             }
26         }  
27     }
28 }
29 
30 public class Demo3 {
31 
32     public static void main(String[] args) {  
33         try {  
34             MyThread t1 = new MyThread("t1");  // 新建“线程t1”
35             System.out.println(t1.getName() +" ("+t1.getState()+") is new.");  
36 
37             t1.start();                      // 启动“线程t1”
38             System.out.println(t1.getName() +" ("+t1.getState()+") is started.");  
39 
40             // 主线程休眠300ms,然后主线程给t1发“中断”指令。
41             Thread.sleep(300);
42             t1.stopTask();
43             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
44 
45             // 主线程休眠300ms,然后查看t1的状态。
46             Thread.sleep(300);
47             System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
48         } catch (InterruptedException e) {  
49             e.printStackTrace();
50         }
51     } 
52 }

运行结果

t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) loop 3
t1 (TERMINATED) is interrupted now.

4. interrupted() 和 isInterrupted()的区别

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

 

本文转载自:

shanxi_liyong
粉丝 11
博文 105
码字总数 37173
作品 0
太原
程序员
私信 提问
读书笔记之《Java并发编程的艺术》-并发编程基础

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
4K
8
Java 多线程系列目录(共43篇)

Java多线程系列目录(共43篇) 最近,在研究Java多线程的内容目录,将其内容逐步整理并发布。 (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 ...

foxeye
2016/02/29
286
0
JAVA基础再回首(二十四)——多线程的概述、实现方式、线程控制、生命周期、多线程程序练习、安全问题的解决

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m366917/article/details/52711906 JAVA基础再回首(二十四)——多线程的概述、实现方式、线程控制、生命周期...

Aduroidpc
2016/09/30
0
0
Java多线程系列--“基础篇”01之 基本概念

多线程是Java中不可避免的一个重要主体。从本章开始,我们将展开对多线程的学习。接下来的内容,是对“JDK中新增JUC包”之前的Java多线程内容的讲解,涉及到的内容包括,Object类中的wait(),...

乔三爷
2015/05/09
146
0
java基础thread——多线程的纷争(循序渐进)

一、多线程概述 进程: 正在运行的程序,是系统进行资源分配和调用的独立单位。 每一个进程都有它自己的内存空间和系统资源。 多进程有什么意义呢? 单进程的计算机只能做一件事情,而我们现在...

潇潇漓燃
2018/05/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

启动参数

常用启动参数,通过 -Dxx.yy=zz注入应用参数 -Deureka.instance.metadata-map.starkGroup=test3 -Dserver.port=8989 本地调试过程中,可改变端口来启动多个相同服务。修改启动的VM参数即可...

ZH-JSON
13分钟前
4
0
ES配置修改

查看配置 GET /_cluster/settings 修改配置 PUT /_cluster/settings{ "persistent" : { "xpack" : { "monitoring" : { "collection" : { "enabled" : ......

messud4312
25分钟前
3
0
Spring事务传播属性有那么难吗?看这一篇就够了

Spring事务传播属性有那么难吗?看这一篇就够了 笔者文笔功力尚浅,如有不妥,请慷慨指出,必定感激不尽 学习东西要知行合一,如果只是知道理论而没实践过,那么掌握的也不会特别扎实,估计过...

不学无数的程序员
25分钟前
4
0
VMware vSphere ESXi主机的访问控制

在vShpere中,访问ESXi主机的途径很多,如下: ESXi DCUI ESXi Shell ESXi SSH ESXi Host Client vCenter --> vSphere web client / vSphere Client VMware vSphere ESXi主机的访问控制,除了......

大别阿郎
50分钟前
5
0
大神讲解CGI、FastCGI和PHP-FPM关系图解

参考资料 概念了解:CGI,FastCGI,PHP-CGI与PHP-FPM:http://www.nowamagic.net/librarys/veda/detail/1319 php中fastcgi和php-fpm是什么东西:https://www.zybuluo.com/phper/note/50231 ......

网络小虾米
59分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部