鍍金池/ 教程/ Java/ 線程中斷
并發(fā)新特性—信號(hào)量 Semaphore
線程間協(xié)作:wait、notify、notifyAll
notify 通知的遺漏
notifyAll 造成的早期通知問(wèn)題
多線程的實(shí)現(xiàn)方法
深入 Java 內(nèi)存模型(1)
多線程環(huán)境下安全使用集合 API
并發(fā)新特性—Lock 鎖與條件變量
生產(chǎn)者—消費(fèi)者模型
深入 Java 內(nèi)存模型(2)
線程中斷
Volatile 關(guān)鍵字(上)
并發(fā)新特性—阻塞隊(duì)列與阻塞棧
可重入內(nèi)置鎖
守護(hù)線程與線程阻塞
并發(fā)新特性—障礙器 CyclicBarrier
Volatile 關(guān)鍵字(下)
synchronized 關(guān)鍵字
synchronized 的另個(gè)一重要作用:內(nèi)存可見(jiàn)性
并發(fā)新特性—Executor 框架與線程池
并發(fā)性與多線程介紹
死鎖
實(shí)現(xiàn)內(nèi)存可見(jiàn)性的兩種方法比較:synchronized 和 Volatile
線程掛起、恢復(fù)與終止

線程中斷

使用 interrupt()中斷線程

當(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í)間差。

使用 isInterrupted()方法判斷中斷狀態(tài)

可以在 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()方法判斷中斷狀態(tài)

可以使用 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ǔ)充

這里補(bǔ)充下 yield 和 join 方法的使用。

  • join 方法用線程對(duì)象調(diào)用,如果在一個(gè)線程 A 中調(diào)用另一個(gè)線程 B 的 join 方法,線程 A 將會(huì)等待線程 B 執(zhí)行完畢后再執(zhí)行。
  • yield 可以直接用 Thread 類調(diào)用,yield 讓出 CPU 執(zhí)行權(quán)給同等級(jí)的線程,如果沒(méi)有相同級(jí)別的線程在等待 CPU 的執(zhí)行權(quán),則該線程繼續(xù)執(zhí)行。