java取消線程實例

本文展現一個常見的取消線程的方法。this

錯誤實例

class BrokenPrimeProducer extends Thread {
    private final BlockingQueue<BigInteger> queue;
    private volatile boolean cancelled = false;

    BrokenPrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!cancelled){
                queue.put(p = p.nextProbablePrime());
            }    
        } catch (InterruptedException consumed) {
        }
    }

    public void cancel() {
        cancelled = true;
    }
}

這裏試圖用一個標誌來跳出while循環,理論上貌似可行,可是這裏使用的是阻塞的操做,那麼就出現一種場景,線程永遠阻塞在put方法,根本就沒來得及下個循環去判斷cancelled這個條件,形成永遠沒法中止掉線程。線程

正確方法

經過中斷來取消線程。設計

public class PrimeProducer extends Thread {
    private final BlockingQueue<BigInteger> queue;

    PrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted()){
                queue.put(p = p.nextProbablePrime());
            }    
        } catch (InterruptedException consumed) {
            /* Allow thread to exit */
        }
    }

    public void cancel() {
        interrupt();
    }
}

這裏的關鍵是queue的put操做可以響應interrupt方法,拋出InterruptedException,倒不是由於while條件裏頭的isInterrupted,這裏while條件換成boolean能夠照樣能夠。code

小結

調用interrupt並不意味着當即中止目標線程正在進行的工做,而只是傳遞了請求中斷的消息。對中斷操做的正確理解是:它並不會真正地中斷一個正在運行的線程,而只是發出中斷請求,而後由線程在下一個合適的時刻中斷本身。it

有些方法,例如wait、sleep和join等,將嚴格地處理這種請求,當它們收到中斷請求或者在開始執行時發現某個已被設置好的中斷狀態時,將拋出一個異常。設計良好的方法能夠徹底忽略這種請求,只要它們能使調用代碼對中斷請求進行某種處理。io

設計糟糕的方法可能會屏蔽中斷請求,從而致使調用棧中的其餘代碼沒法對中斷請求做出響應。在使用靜態的interrupted時應該當心,由於它會清除當前線程的中斷狀態。若是在調用interrupted時返回了true,那麼除非你想屏蔽這個中斷,不然必須對它進行處理—能夠拋出InterruptedException,或者經過再次調用interrupt來恢復中斷狀態。class

相關文章
相關標籤/搜索