interrupt()的字面意思是中斷一個線程,那么它是怎么使用來達到中斷當前線程的呢?我們來看幾個例子。
一、終止處于“阻塞狀態”的線程
通過中斷方式終止處于阻塞狀態的線程,當線程由于被調用了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)
循環。
對InterruptException
的捕獲一定要放在while(true)
循環體的外面,這樣產生異常時就退出了while(true)
循環,否則,InterruptException
在while(true)
循環體之外,就需要額外的添加退出處理,形式如下:
@Override
public void run() {
while (true) {
try {
// 執行任務...
} catch (InterruptedException ie) {
// InterruptedException在while(true)循環體內。
// 當線程產生了InterruptedException異常時,while(true)仍能繼續運行!需要手動退出
break;
}
}
}
上面的
InterruptedException
異常的捕獲是在while(true)
中,當產生異常被catch
時,仍然在while(true)
循環體內,要退出while(true)
循環體,需要額外的執行操作。
二、終止處于運行狀態的線程
通過標記方式終止處于運行狀態的線程,其中,包括“中斷標記”和“額外添加標記”
(1)通過“中斷標記”終止線程,形式如下:
@Override
public void run() {
while (!isInterrupted()) {
// 執行任務...
}
}
說明:
isInterrupted()
是判斷線程的中斷標記是不是為true,當前線程處于運行狀態,并且我們需要終止它時,可以調用線程的interrupt()
方法,使用線程的中斷標記為true,即isInterrupted()
會返回true
,此時,就會退出while
循環。interrupt()
并不會終止處于“運行狀態”的線程,它會將線程的中斷標記設為true
(2)通過“額外添加標記”,形式如下:
rivate 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()
常常被用來終止“阻塞狀態”線程,參考示例:
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
try {
int i=0;
while (!isInterrupted())
{
System.out.println("thread is running");
Thread.sleep(100); // 休眠100ms
i++;
System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");
}
}
}
public class Hello {
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發“中斷”指令。
System.out.println("MainThread sleep");
Thread.sleep(300);
System.out.println("Thread interrupt");
t1.interrupt();
System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
// 主線程休眠300ms,然后查看t1的狀態。
System.out.println("MainThread sleep");
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.
MainThread sleep
thread is running
t1 (RUNNABLE) loop 1
thread is running
t1 (RUNNABLE) loop 2
thread is running
t1 (RUNNABLE) loop 3
thread is running
Thread interrupt
t1 (TIMED_WAITING) is interrupted.
MainThread sleep
t1 (RUNNABLE) catch InterruptedException.
t1 (TERMINATED) is interrupted now.
結果說明:
(1)主線程main中會通過new MyThread("t1")創建線程t1,之后通過t1.start()啟動線程t1
(2)t1啟動之后,會不斷的檢查他的中斷標記,如果中斷標記為false,則休眠100ms
(3)t1休眠之后會切換到主線程main,主線程再次運行時,會執行t1.interrupt()中斷線程t1。t1收到中斷指令之后,會將t1的中斷標志設置為false,而且會拋出InterruptedException
異常,在t1的run()方法中,是在循環體之外捕獲的異常,因此循環被終止。
通過“額外添加標記”的方式終止“運行狀態”的線程的示例:
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) {
try {
int i=0;
while (flag) {
Thread.sleep(100); // 休眠100ms
i++;
System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);
}
} catch (InterruptedException ie) {
System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");
}
}
}
}
public class Hello {
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);
System.out.println("stopTask");
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();
}
}
}
interrupted()
和isInterrupted()
都能夠用于檢測對象的“中斷標記”,區別是interrupted()
除了返回中斷標記外,它還會清除中斷標記(即將中斷標記設為false),而isInterrupted()
僅僅返回中斷標記,關于這兩個方法的詳細解釋請看后續文章。