當(dāng)一個(gè)線程運(yùn)行時(shí),另一個(gè)線程可以調(diào)用對(duì)應(yīng)的 Thread 對(duì)象的 interrupt()方法來(lái)中斷它,該方法只是在目標(biāo)線程中設(shè)置一個(gè)標(biāo)志,表示它已經(jīng)被中斷,并立即返回。這里需要注意的是,如果只是單純的調(diào)用 interrupt()方法,線程并沒(méi)有實(shí)際被中斷,會(huì)繼續(xù)往下執(zhí)行。
下面一段代碼演示了休眠線程的中斷:
public class SleepInterrupt extends Object implements Runnable{
public void run(){
try{
System.out.println("in run() - about to sleep for 20 seconds");
Thread.sleep(20000);
System.out.println("in run() - woke up");
}catch(InterruptedException e){
System.out.println("in run() - interrupted while sleeping");
//處理完中斷異常后,返回到run()方法人口,
//如果沒(méi)有return,線程不會(huì)實(shí)際被中斷,它會(huì)繼續(xù)打印下面的信息
return;
}
System.out.println("in run() - leaving normally");
}
public static void main(String[] args) {
SleepInterrupt si = new SleepInterrupt();
Thread t = new Thread(si);
t.start();
//主線程休眠2秒,從而確保剛才啟動(dòng)的線程有機(jī)會(huì)執(zhí)行一段時(shí)間
try {
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("in main() - interrupting other thread");
//中斷線程t
t.interrupt();
System.out.println("in main() - leaving");
}
}
運(yùn)行結(jié)果如下:
http://wiki.jikexueyuan.com/project/java-concurrency/images/result2.png" alt="" />
主線程啟動(dòng)新線程后,自身休眠 2 秒鐘,允許新線程獲得運(yùn)行時(shí)間。新線程打印信息about to sleep for 20 seconds
后,繼而休眠 20 秒鐘,大約 2 秒鐘后,main 線程通知新線程中斷,那么新線程的 20 秒的休眠將被打斷,從而拋出 InterruptException 異常,執(zhí)行跳轉(zhuǎn)到 catch 塊,打印出interrupted while sleeping
信息,并立即從 run()方法返回,然后消亡,而不會(huì)打印出 catch 塊后面的leaving normally
信息。
請(qǐng)注意:由于不確定的線程規(guī)劃,上圖運(yùn)行結(jié)果的后兩行可能順序相反,這取決于主線程和新線程哪個(gè)先消亡。但前兩行信息的順序必定如上圖所示。
另外,如果將 catch 塊中的 return 語(yǔ)句注釋掉,則線程在拋出異常后,會(huì)繼續(xù)往下執(zhí)行,而不會(huì)被中斷,從而會(huì)打印出leaving normally
信息。
在上面的例子中,sleep()方法的實(shí)現(xiàn)檢查到休眠線程被中斷,它會(huì)相當(dāng)友好地終止線程,并拋出 InterruptedException 異常。另外一種情況,如果線程在調(diào)用 sleep()方法前被中斷,那么該中斷稱為待決中斷,它會(huì)在剛調(diào)用 sleep()方法時(shí),立即拋出 InterruptedException 異常。
下面的代碼演示了待決中斷:
public class PendingInterrupt extends Object {
public static void main(String[] args){
//如果輸入了參數(shù),則在mian線程中中斷當(dāng)前線程(亦即main線程)
if( args.length > 0 ){
Thread.currentThread().interrupt();
}
//獲取當(dāng)前時(shí)間
long startTime = System.currentTimeMillis();
try{
Thread.sleep(2000);
System.out.println("was NOT interrupted");
}catch(InterruptedException x){
System.out.println("was interrupted");
}
//計(jì)算中間代碼執(zhí)行的時(shí)間
System.out.println("elapsedTime=" + ( System.currentTimeMillis() - startTime));
}
}
如果 PendingInterrupt 不帶任何命令行參數(shù),那么線程不會(huì)被中斷,最終輸出的時(shí)間差距應(yīng)該在 2000 附近(具體時(shí)間由系統(tǒng)決定,不精確),如果 PendingInterrupt 帶有命令行參數(shù),則調(diào)用中斷當(dāng)前線程的代碼,但 main 線程仍然運(yùn)行,最終輸出的時(shí)間差距應(yīng)該遠(yuǎn)小于 2000,因?yàn)榫€程尚未休眠,便被中斷,因此,一旦調(diào)用 sleep()方法,會(huì)立即打印出 catch 塊中的信息。
執(zhí)行結(jié)果如下:
http://wiki.jikexueyuan.com/project/java-concurrency/images/result3.png" alt="" />
這種模式下,main 線程中斷它自身。除了將中斷標(biāo)志(它是 Thread 的內(nèi)部標(biāo)志)設(shè)置為 true 外,沒(méi)有其他任何影響。線程被中斷了,但 main 線程仍然運(yùn)行,main 線程繼續(xù)監(jiān)視實(shí)時(shí)時(shí)鐘,并進(jìn)入 try 塊,一旦調(diào)用 sleep()方法,它就會(huì)注意到待決中斷的存在,并拋出 InterruptException。于是執(zhí)行跳轉(zhuǎn)到 catch 塊,并打印出線程被中斷的信息。最后,計(jì)算并打印出時(shí)間差。
可以在 Thread 對(duì)象上調(diào)用 isInterrupted()方法來(lái)檢查任何線程的中斷狀態(tài)。這里需要注意:線程一旦被中斷,isInterrupted()方法便會(huì)返回 true,而一旦 sleep()方法拋出異常,它將清空中斷標(biāo)志,此時(shí)isInterrupted()方法將返回 false。
下面的代碼演示了 isInterrupted()方法的使用:
public class InterruptCheck extends Object{
public static void main(String[] args){
Thread t = Thread.currentThread();
System.out.println("Point A: t.isInterrupted()=" + t.isInterrupted());
//待決中斷,中斷自身
t.interrupt();
System.out.println("Point B: t.isInterrupted()=" + t.isInterrupted());
System.out.println("Point C: t.isInterrupted()=" + t.isInterrupted());
try{
Thread.sleep(2000);
System.out.println("was NOT interrupted");
}catch( InterruptedException x){
System.out.println("was interrupted");
}
//拋出異常后,會(huì)清除中斷標(biāo)志,這里會(huì)返回false
System.out.println("Point D: t.isInterrupted()=" + t.isInterrupted());
}
}
運(yùn)行結(jié)果如下:
http://wiki.jikexueyuan.com/project/java-concurrency/images/result4.png" alt="" />
可以使用 Thread.interrupted()方法來(lái)檢查當(dāng)前線程的中斷狀態(tài)(并隱式重置為 false)。又由于它是靜態(tài)方法,因此不能在特定的線程上使用,而只能報(bào)告調(diào)用它的線程的中斷狀態(tài),如果線程被中斷,而且中斷狀態(tài)尚不清楚,那么,這個(gè)方法返回 true。與 isInterrupted()不同,它將自動(dòng)重置中斷狀態(tài)為 false,第二次調(diào)用 Thread.interrupted()方法,總是返回 false,除非中斷了線程。
如下代碼演示了 Thread.interrupted()方法的使用:
public class InterruptReset extends Object {
public static void main(String[] args) {
System.out.println(
"Point X: Thread.interrupted()=" + Thread.interrupted());
Thread.currentThread().interrupt();
System.out.println(
"Point Y: Thread.interrupted()=" + Thread.interrupted());
System.out.println(
"Point Z: Thread.interrupted()=" + Thread.interrupted());
}
}
運(yùn)行結(jié)果如下:
http://wiki.jikexueyuan.com/project/java-concurrency/images/result5.png" alt="" />
從結(jié)果中可以看出,當(dāng)前線程中斷自身后,在 Y 點(diǎn),中斷狀態(tài)為 true,并由 Thread.interrupted()自動(dòng)重置為 false,那么下次調(diào)用該方法得到的結(jié)果便是 false。
這里補(bǔ)充下 yield 和 join 方法的使用。