線程中斷

為什么要中斷

  • Java中沒有一種安全的搶占方法來停止線程槐臀,也沒有安全的搶占方式停止任務(wù),只有一些協(xié)作機制氓仲。
  • 更好的支持任務(wù)的取消關(guān)閉

可以取消的任務(wù)

  • 下面任務(wù)名稱:在規(guī)定時間內(nèi)搜索素數(shù)水慨。
  • 任務(wù)特點:任務(wù)最終會結(jié)束。
  • PrimeGenerator持續(xù)地枚舉素數(shù)敬扛,直到被取消晰洒。cancel放在finally中確保cancel最終會被調(diào)用。
  • PrimeGenerator使用了一種簡單的取消策略:客戶端代碼通過cancel來請求取消啥箭,PrimeGenerator在每次搜索素數(shù)前首先檢查是否存在取消請求谍珊,如果存在則退出。
public class PrimeGenerator implements Runnable {
    private static ExecutorService exec = Executors.newCachedThreadPool();

    @GuardedBy("this") private final List<BigInteger> primes  = new ArrayList<BigInteger>();
    private volatile boolean cancelled;

    public void run() {
        BigInteger p = BigInteger.ONE;
        while (!cancelled) {
            p = p.nextProbablePrime();
            synchronized (this) {
                primes.add(p);
            }
        }
    }

    public void cancel() {
        cancelled = true;
    }

    public synchronized List<BigInteger> get() {
        return new ArrayList<BigInteger>(primes);
    }

    static List<BigInteger> aSecondOfPrimes() throws InterruptedException {
        PrimeGenerator generator = new PrimeGenerator();
        exec.execute(generator);                        //缺點:如果generator拋出未受檢異常急侥,該異常在此處無法撲捉到
        try {
            SECONDS.sleep(1);
        } finally {
            generator.cancel();
        }
        return generator.get();
    }

永遠不會結(jié)束的任務(wù)

  • 下面任務(wù)名稱:生產(chǎn)者-消費者模式砌滞,生產(chǎn)者生產(chǎn)素數(shù),消費者消費素數(shù)坏怪,使用阻塞隊列贝润。
  • 任務(wù)特點:存在無法結(jié)束情況。
  • 同樣通過cancelled來作為取消標(biāo)志铝宵,但是run任務(wù)中存在阻塞方法put,如果生產(chǎn)者速度超過消費者速度打掘,隊列被填滿,put方法阻塞,當(dāng)put方法阻塞時胧卤,如果消費者希望取消生產(chǎn)者任務(wù)唯绍,那么消費者可以調(diào)用cancel來設(shè)置cancelled標(biāo)志,但是此時生產(chǎn)者卻永遠不能檢查這個標(biāo)志枝誊,因為它無法從阻塞的put方法中恢復(fù)過來况芒。
  • 說明了自定義的取消機制無法與可阻塞的庫函數(shù)實現(xiàn)良好的交互。
  • 解決方法:使用中斷而不是boolean標(biāo)志來請求取消叶撒。(見下面代碼)
public 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;
    }
    void consumePrimes(){
        BlockingQueue<BigInteger> primes = new LinkedBlockingQueue<BigInteger>(3);
        BrokenPrimeProducer primeProducer = new BrokenPrimeProducer(primes);
        primeProducer.start();       //缺點:如果generator拋出未受檢異常绝骚,該異常在此處無法撲捉到
        try{
            while(needMorePrimes()){
                consume(primes.take());
            }
        }finally {
            primeProducer.cancel();;
        }
    }
}

Thread中的中斷方法

  • 每個線程都有一個boolean類型的中斷狀態(tài),當(dāng)中斷線程時祠够,這個線程的中斷狀態(tài)將被設(shè)置為true压汪。線程可以查詢中斷狀態(tài)根據(jù)需求來做響應(yīng)處理。

  • interrupt :中斷目標(biāo)線程

  • isInterrupted:返回目標(biāo)線程的中斷狀態(tài)

  • interrupted (靜態(tài)方法):清除當(dāng)前線程的中斷狀態(tài)古瓤,并返回它之前的值止剖,這是清除中斷狀態(tài)的唯一方法。

  • 中斷不會真正中斷一個正在運行的線程落君,只是發(fā)出中斷請求穿香,然后由線程在下一個合適的時刻中斷自己。

阻塞方法如何處理中斷

  • 阻塞庫方法绎速,如Thread.sleep皮获、Object.wait,阻塞隊列的take纹冤、put方法等等洒宝,都會檢查當(dāng)前線程中斷狀態(tài)(即執(zhí)行該代碼的線程),并且在發(fā)現(xiàn)中斷時提前返回萌京。它們在響應(yīng)中斷時執(zhí)行的操作包括:清除中斷狀態(tài)奖蔓,拋出InterruptedException壳快,表示阻塞操作由于中斷而提前結(jié)束硼婿。
  • 注:并不是所有的阻塞方法都會通過提前返回或拋出異常來響應(yīng)中斷請求(如Socket I/O)
  • JVM并不能保證阻塞方法檢測到中斷的速度辜御,但在實際情況中響應(yīng)速度還非橙鲶埃快的藐守。

如何處理阻塞方法拋出的InterruptedException異常

  • 1.傳遞InterruptedException:只需把InterruptedException傳遞給調(diào)用方法的調(diào)用者荸频。傳遞包括:根本不捕獲該異常留晚,通過throws拋印蔗“亲睿或者捕獲異常,然后在執(zhí)行某種清理工作后再次拋出這個異常华嘹。
  • 2.恢復(fù)中斷:有時候不能拋出InterruptedException吧趣,例如代碼是Runnable的一部分,這時必須捕獲異常,并通過調(diào)用當(dāng)前線程上的interrupt方法恢復(fù)中斷狀態(tài)(阻塞方法處理中斷會清除中斷狀態(tài))强挫,這樣調(diào)用棧中更高層代碼將看到引發(fā)了一個中斷岔霸。(只有調(diào)用isInterrupted才可以看到)。
public class TaskRunnable implements Runnable {
    BlockingQueue<Task> queue;

    public void run() {
        try {
            processTask(queue.take());
        } catch (InterruptedException e) {
            // 恢復(fù)中斷狀態(tài)
            Thread.currentThread().interrupt();
        }
    }
    void processTask(Task task) {
        // Handle the task
    }
    interface Task {
    }
}
  • 3.屏蔽中斷:只有一種情況才可以屏蔽中斷俯渤,只有實現(xiàn)了線程中斷策略的代碼才可以屏蔽中斷請求呆细,在常規(guī)的任務(wù)和代碼庫中都不應(yīng)該屏蔽中斷請求。(見下面PrimeProducer類的代碼)
  • 4.其它方法八匠,根據(jù)需求定絮爷。
    • 下面代碼的演示了不可以取消的任務(wù),在發(fā)生中斷時該如何響應(yīng)中斷情況梨树,代碼中有阻塞方法take坑夯,如果線程被中斷就會使take拋出InterruptedException異常,但是任務(wù)不可以取消抡四,所以發(fā)生中斷后通過重試
public class NoncancelableTask {
    public Task getNextTask(BlockingQueue<Task> queue) {
        boolean interrupted = false;
        try {
            while (true) {
                try {
                    return queue.take();
                } catch (InterruptedException e) {
                    interrupted = true;
                    // fall through and retry
                }
            }
        } finally {
            if (interrupted)
                Thread.currentThread().interrupt();
        }
    }
    interface Task {
    }
}

使用中斷解決上面“永遠不會結(jié)束的任務(wù)”

  • 雖然PrimeProducer屏蔽了中斷柜蜈,這是因為它知道線程將要結(jié)束(通過while (!Thread.currentThread().isInterrupted())代碼),即PrimeProducer實現(xiàn)了線程的中斷策略指巡,所以線程被中斷后淑履,雖然put方法會拋出中斷異常,但是那是合理的厌处,因為即使不拋出異常鳖谈,while方法中通過檢查也會發(fā)現(xiàn)線程被中斷,從而結(jié)束線程阔涉。
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) {
            /* 允許線程退出 */
        }
    }

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

使用Future來取消任務(wù)

  • 針對前面的案例缆娃,如果在run中的任務(wù)發(fā)生了未檢查異常,則該對于線程調(diào)用者來說該異常會被忽略掉瑰排,因為其運行在單獨的線程中贯要,如果想將異常拋出以便調(diào)用者來處理,則該使用Future來處理椭住。
  • Future的cancel方法:接收boolean類型參數(shù)mayInterrptIfRuning,表示任務(wù)是否能夠接收中斷崇渗,如果為true,并且任務(wù)當(dāng)前正在某個線程中運行京郑,那么這個線程能被中斷宅广。如果這個參數(shù)為false,那么“若任務(wù)還沒啟動些举,就不要運行”跟狱。
  • 除非你清楚線程中斷策略,否則不要中斷線程户魏,那么在什么情況下可以將cancel參數(shù)設(shè)為true?執(zhí)行任務(wù)的線程由標(biāo)準(zhǔn)的Executor創(chuàng)建的驶臊,它實現(xiàn)了一種中斷策略使得任務(wù)可以通過中斷被取消挪挤。
public class TimedRun {
    private static final ExecutorService taskExec = Executors.newCachedThreadPool();

    public static void timedRun(Runnable r,
                                long timeout, TimeUnit unit)
            throws InterruptedException {
        Future<?> task = taskExec.submit(r);
        try {
            task.get(timeout, unit);
        } catch (TimeoutException e) {
            // 接下來任務(wù)將被取消,放在了finally處理
        } catch (ExecutionException e) {
            // 如果在任務(wù)中拋出異常关翎,那么重新拋出該異常
            throw launderThrowable(e.getCause());
        } finally {
            // Harmless if task already completed
            task.cancel(true); // interrupt if running
        }
    }

 public static RuntimeException launderThrowable(Throwable t) {
        if (t instanceof RuntimeException)
            return (RuntimeException) t;
        else if (t instanceof Error)
            throw (Error) t;
        else
            throw new IllegalStateException("Not unchecked", t);
    }
}

中斷策略

  • 中斷策略規(guī)定線程如何解釋某個中斷請求---當(dāng)發(fā)現(xiàn)中斷請求時扛门,應(yīng)該做哪些工作,哪些工作單元對于中斷來說是原子性操作纵寝,以及以多快的速度來響應(yīng)中斷论寨。

處理不可中斷的阻塞

  • 不可中斷的阻塞方法
  • 不可中斷的阻塞方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市爽茴,隨后出現(xiàn)的幾起案子政基,更是在濱河造成了極大的恐慌,老刑警劉巖闹啦,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沮明,死亡現(xiàn)場離奇詭異,居然都是意外死亡窍奋,警方通過查閱死者的電腦和手機荐健,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琳袄,“玉大人江场,你說我怎么就攤上這事〗讯海” “怎么了址否?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碎紊。 經(jīng)常有香客問我佑附,道長,這世上最難降的妖魔是什么仗考? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任音同,我火速辦了婚禮,結(jié)果婚禮上秃嗜,老公的妹妹穿的比我還像新娘权均。我一直安慰自己,他們只是感情好锅锨,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布叽赊。 她就那樣靜靜地躺著,像睡著了一般必搞。 火紅的嫁衣襯著肌膚如雪必指。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天顾画,我揣著相機與錄音取劫,去河邊找鬼。 笑死研侣,一個胖子當(dāng)著我的面吹牛谱邪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播庶诡,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼惦银,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了末誓?” 一聲冷哼從身側(cè)響起扯俱,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎喇澡,沒想到半個月后迅栅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡晴玖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年读存,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呕屎。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡让簿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秀睛,到底是詐尸還是另有隱情尔当,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布蹂安,位于F島的核電站椭迎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏田盈。R本人自食惡果不足惜侠碧,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缠黍。 院中可真熱鬧弄兜,春花似錦、人聲如沸瓷式。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贸典。三九已至视卢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間廊驼,已是汗流浹背据过。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工惋砂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绳锅。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓西饵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鳞芙。 傳聞我的和親對象是個殘疾皇子眷柔,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 一、前言 所謂線程中斷原朝,其實就是終止一個線程驯嘱。在使用 Java 線程時,除了線程自行正常結(jié)束喳坠,很多時候也需要提前結(jié)...
    編走編想閱讀 1,516評論 2 9
  • 什么是線程中斷 Java中斷機制是一種協(xié)作機制鞠评,也就是說通過中斷并不能直接終止另一個線程,而需要被中斷的線程自己處...
    驪驊閱讀 520評論 0 1
  • 取消任務(wù)的方式 Java中沒有提供任何機制來安全地終止線程,但是提供了中斷(Interruption)協(xié)作機制,能...
    Showdy閱讀 496評論 0 6
  • 轉(zhuǎn)載請注明出處: 源碼分析glide對線程中斷的優(yōu)化 地址:http://www.reibang.com/p/e0...
    陳文超happylion閱讀 792評論 0 1
  • 前面的幾篇文章主要介紹了線程的一些最基本的概念壕鹉,包括線程的間的沖突及其解決辦法谢澈,以及線程間的協(xié)作機制。本篇主要來學(xué)...
    Single_YAM閱讀 472評論 0 3