深入分析JAVA 多線程--interrupt()和線程終止方式
一、interrupt() 介紹
interrupt() 定義在 Thread 類中,作用是中斷本線程。
本線程中斷自己是被允許的;其它線程調(diào)用本線程的 interrupt() 方法時,會通過 checkAccess() 檢查權(quán)限。這有可能拋出 SecurityException 異常。
如果本線程是處于阻塞狀態(tài):調(diào)用線程的 wait() , wait(long) 或 wait(long, int) 會讓它進入等待(阻塞)狀態(tài),或者調(diào)用線程的 join(),join(long),join(long, int),sleep(long),sleep(long, int) 也會讓它進入阻塞狀態(tài)。若線程在阻塞狀態(tài)時,調(diào)用了它的 interrupt() 方法,那么它的“中斷狀態(tài)”會被清除并且會收到一個 InterruptedException 異常。例如,線程通過 wait() 進入阻塞狀態(tài),此時通過 interrupt() 中斷該線程;調(diào)用 interrupt() 會立即將線程的中斷標記設(shè)為 true,但是由于線程處于阻塞狀態(tài),所以該“中斷標記”會立即被清除為 “false”,同時,會產(chǎn)生一個 InterruptedException 的異常。
如果線程被阻塞在一個 Selector 選擇器中,那么通過 interrupt() 中斷它時;線程的中斷標記會被設(shè)置為 true,并且它會立即從選擇操作中返回。
如果不屬于前面所說的情況,那么通過 interrupt() 中斷線程時,它的中斷標記會被設(shè)置為 true。
中斷一個“已終止的線程”不會產(chǎn)生任何操作。
二、線程終止方式
Thread中的 stop() 和 suspend() 方法,由于固有的不安全性,已經(jīng)建議不再使用!
下面,我先分別討論線程在“阻塞狀態(tài)”和“運行狀態(tài)”的終止方式,然后再總結(jié)出一個通用的方式。
(一)、終止處于“阻塞狀態(tài)”的線程.
通常,我們通過“中斷”方式終止處于“阻塞狀態(tài)”的線程。
當線程由于被調(diào)用了 sleep(),,wait(),join() 等方法而進入阻塞狀態(tài);若此時調(diào)用線程的 interrupt() 將線程的中斷標記設(shè)為 true。由于處于阻塞狀態(tài),中斷標記會被清除,同時產(chǎn)生一個InterruptedException 異常。將 InterruptedException 放在適當?shù)奈恢镁湍芙K止線程,形式如下:
public void run() {
try {
while (true) {
// 執(zhí)行業(yè)務(wù)
}
} catch (InterruptedException ie) {
// 由于產(chǎn)生InterruptedException異常,退出while(true)循環(huán),線程終止!
}
}
說明:
在while(true)中不斷的執(zhí)行業(yè)務(wù)代碼,當線程處于阻塞狀態(tài)時,調(diào)用線程的 interrupt() 產(chǎn)生 InterruptedException 中斷。中斷的捕獲在 while(true) 之外,這樣就退出了 while(true) 循環(huán)!
注意:
對 InterruptedException 的捕獲務(wù)一般放在 while(true) 循環(huán)體的外面,這樣,在產(chǎn)生異常時就退出了 while(true) 循環(huán)。否則,InterruptedException 在 while(true) 循環(huán)體之內(nèi),就需要額外的添加退出處理。形式如下:
public void run() {
while (true) {
try {
// 執(zhí)行任務(wù)...
} catch (InterruptedException ie) {
// InterruptedException在while(true)循環(huán)體內(nèi)。
// 當線程產(chǎn)生了InterruptedException異常時,while(true)仍能繼續(xù)運行!需要手動退出
break;
}
}
}
說明:
上面的 InterruptedException 異常的捕獲在 whle(true) 之內(nèi)。當產(chǎn)生 InterruptedException 異常時,被 catch 處理之外,仍然在 while(true) 循環(huán)體內(nèi);要退出 while(true) 循環(huán)體,需要額外的執(zhí)行退出while(true) 的操作。
(二)、終止處于“運行狀態(tài)”的線程
通常,我們通過“標記”方式終止處于“運行狀態(tài)”的線程。其中,包括“中斷標記”和“額外添加標記”。
1、通過“中斷標記”終止線程
public void run() {
while (!isInterrupted()) {
// 執(zhí)行任務(wù)...
}
}
說明:
isInterrupted() 是判斷線程的中斷標記是不是為 true。當線程處于運行狀態(tài),并且我們需要終止它時;可以調(diào)用線程的 interrupt() 方法,使用線程的中斷標記為 true,即 isInterrupted() 會返回true。此時,就會退出while循環(huán)。
注意:interrupt() 并不會終止處于“運行狀態(tài)”的線程!它會將線程的中斷標記設(shè)為 true。
2、通過“額外添加標記”終止線程
private volatile boolean flag= true;
protected void stopTask() {
flag = false;
}
public void run() {
while (flag) {
// 執(zhí)行任務(wù)...
}
}
說明:
線程中有一個 flag 標記,它的默認值是 true;并且我們提供 stopTask() 來設(shè)置 flag 標記。當我們需要終止該線程時,調(diào)用該線程的 stopTask() 方法就可以讓線程退出 while 循環(huán)。
注意:將 flag 定義為 volatile 類型,是為了保證 flag 的可見性。即其它線程通過 stopTask() 修改了 flag 之后,本線程能看到修改后的 flag 的值。
(三)、通過方式
綜合線程處于“阻塞狀態(tài)”和“運行狀態(tài)”的終止方式,比較通用的終止線程的形式如下:
public void run() {
try {
// 1. isInterrupted()保證,只要中斷標記為true就終止線程。
while (!isInterrupted()) {
// 執(zhí)行任務(wù)...
}
} catch (InterruptedException ie) {
// 2. InterruptedException異常保證,當InterruptedException異常產(chǎn)生時,線程被終止。
}
}
1、isInterrupted()保證,只要中斷標記為 true 就終止線程。
2、InterruptedException 異常保證,當 InterruptedException 異常產(chǎn)生時,線程被終止。
三、示例
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發(fā)“中斷”指令,查看t1狀態(tài)
t1.interrupt();
System.out.println(t1.getName() + "[" + t1.getState() + "] is interrupted.");
Thread.sleep(300);// 休眠300毫秒,然后查看t1狀態(tài)
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");
}
}
}
運行結(jié)果
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") 創(chuàng)建線程 t1,之后通過 t1.start() 啟動線程 t1。
②、t1 啟動之后,會不斷的檢查它的中斷標記,如果中斷標記為“false”;則休眠 100ms。
③、t1 休眠之后,會切換到主線程main;主線程再次運行時,會執(zhí)行t1.interrupt()中斷線程t1。t1收到中斷指令之后,會將t1的中斷標記設(shè)置“false”,而且會拋出 InterruptedException 異常。在 t1 的 run() 方法中,是在循環(huán)體 while 之外捕獲的異常;因此循環(huán)被終止。
我們對上面的結(jié)果進行小小的修改,將run()方法中捕獲InterruptedException異常的代碼塊移到while循環(huán)體內(nèi)。
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發(fā)“中斷”指令,查看t1狀態(tài)
t1.interrupt();
System.out.println(t1.getName() + " [ " + t1.getState() + " ] is interrupted.");
Thread.sleep(300);// 休眠300毫秒,然后查看t1狀態(tài)
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);
}
}
}
運行結(jié)果
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
...... // 無限循環(huán)
說明:
程序進入了死循環(huán)了。
這是因為,t1在“等待(阻塞)狀態(tài)”時,被 interrupt() 中斷;此時,會清除中斷標記(即 isInterrupted() 會返回 false),而且會拋出 InterruptedException 異常(該異常在while循環(huán)體內(nèi)被捕獲)。因此,t1理所當然的會進入死循環(huán)了。
解決該問題,需要我們在捕獲異常時,額外的進行退出 while 循環(huán)的處理。例如,在 MyThread 的 catch(InterruptedException) 中添加 break 或 return 就能解決該問題。
下面是通過“額外添加標記”的方式終止“狀態(tài)狀態(tài)”的線程的示例:
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發(fā)“中斷”指令,查看t1狀態(tài)
t1.stopTask();
System.out.println(t1.getName() + " [ " + t1.getState() + " ] is interrupted.");
Thread.sleep(300);// 休眠300毫秒,然后查看t1狀態(tài)
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);
}
}
}
}
運行結(jié)果
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()的區(qū)別
interrupted() 和 isInterrupted()都能夠用于檢測對象的“中斷標記”。
區(qū)別是,interrupted() 除了返回中斷標記之外,它還會清除中斷標記(即將中斷標記設(shè)為 false);而 isInterrupted() 僅僅返回中斷標記。
以上就是深入分析JAVA 多線程--interrupt()和線程終止方式的詳細內(nèi)容,更多關(guān)于JAVA 多線程--interrupt()和線程終止的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
MyBatis Plus插件機制與執(zhí)行流程原理分析詳解
這篇文章主要介紹了MyBatis Plus插件機制與執(zhí)行流程原理分析,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
spring boot整合mybatis+mybatis-plus的示例代碼
這篇文章主要介紹了spring boot整合mybatis+mybatis-plus的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01
Java web入門指南之在Idea上創(chuàng)建Java web項目
好多書上的JavaWeb教程都是Eclipse以及MyEclipse,當然這里不論IDE的好壞,下面這篇文章主要給大家介紹了關(guān)于Java web入門指南之在Idea上創(chuàng)建Java web項目的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-06-06

