Java多線程之線程中斷

取消任務(wù)的方式

Java中沒(méi)有提供任何機(jī)制來(lái)安全地終止線程,但是提供了中斷(Interruption)協(xié)作機(jī)制,能夠使一個(gè)線程終止另一個(gè)線程的當(dāng)前工作. 一般取消或停止某個(gè)任務(wù),很少采用立即停止,因?yàn)榱⒓赐V箷?huì)使得共享數(shù)據(jù)結(jié)構(gòu)出于不一致的狀態(tài).這也是Thread.stop(),Thread.suspend()以及Thread.resume()不安全的原因而廢棄.

Java中有三種方式可以終止當(dāng)前運(yùn)行的線程:

  • 設(shè)置某個(gè)"已請(qǐng)求取消(Cancellation Requested)"標(biāo)記,而任務(wù)將定期查看該標(biāo)記的協(xié)作機(jī)制來(lái)中斷線程.
  • 使用Thread.stop()強(qiáng)制終止線程,但是因?yàn)檫@個(gè)方法"解鎖"導(dǎo)致共享數(shù)據(jù)結(jié)構(gòu)處于不一致而不安全被廢棄.
  • 使用Interruption中斷機(jī)制.

使用中斷標(biāo)記來(lái)中斷線程.


    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);
            try {
                SECONDS.sleep(1);
            } finally {
                generator.cancel();
            }
            return generator.get();
        }
    }

設(shè)置標(biāo)記的中斷策略: PrimeGenerator使用一種簡(jiǎn)單取消策略,客戶(hù)端代碼通過(guò)調(diào)研cancel來(lái)請(qǐng)求取消,PrimeGenerator在每次搜索素?cái)?shù)時(shí)前先檢查是否存在取消請(qǐng)求,如果不存在就退出.

但是使用設(shè)置標(biāo)記的中斷策略有一問(wèn)題: 如果任務(wù)調(diào)用調(diào)用阻塞的方法,比如BlockingQueue.put,那么可能任務(wù)永遠(yuǎn)不會(huì)檢查取消標(biāo)記而不會(huì)結(jié)束.


    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)
                    //此處阻塞,可能永遠(yuǎn)無(wú)法檢測(cè)到結(jié)束的標(biāo)記
                    queue.put(p = p.nextProbablePrime());
            } catch (InterruptedException consumed) {
            }
        }
    
        public void cancel() {
            cancelled = true;
        }
    }

解決辦法也很簡(jiǎn)單: 使用中斷而不是使用boolean標(biāo)記來(lái)請(qǐng)求取消

使用中斷(Interruption)請(qǐng)求取消

  • Thread類(lèi)中的中斷方法:
    • public void interrupt()

      請(qǐng)求中斷,設(shè)置中斷標(biāo)記,而并不是真正中斷一個(gè)正在運(yùn)行的線程,只是發(fā)出了一個(gè)請(qǐng)求中斷的請(qǐng)求,由線程在合適的時(shí)候中斷自己.

    • public static native boolean interrupted();

      判斷線程是否中斷,會(huì)擦除中斷標(biāo)記(判斷的是當(dāng)前運(yùn)行的線程),另外若調(diào)用Thread.interrupted()返回為true時(shí),必須要處理,可以拋出中斷異嘲简冢或者再次調(diào)用interrupt()來(lái)恢復(fù)中斷.

    • public native boolean isInterrupted();

      判斷線程是否中斷,不會(huì)擦除中斷標(biāo)記

故而上面問(wèn)題的解決方案如下:


    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();
        }
    }

  • 那么thread.interrupt()調(diào)用后到底意味著什么?

    首先一個(gè)線程不應(yīng)該由其他線程來(lái)強(qiáng)制中斷或者停止,而應(yīng)該由線程自己停止,所以Thread.stop(),Thread.suspend(),Thread.resume()都已被廢棄.而{@link Thread#interrupt}作用其實(shí)不是中斷線程,而請(qǐng)求線程中斷.具體來(lái)說(shuō),當(dāng)調(diào)用interrupt()方法時(shí):

    • 如果線程處于阻塞狀態(tài)時(shí)(例如處于sleep,wait,join等狀態(tài)時(shí))那么線程將立即退出阻塞狀態(tài)而拋出InterruptedException異常.
    • 如果 線程處于正呈阆撸活動(dòng)狀態(tài),那么會(huì)將線程的中斷標(biāo)記設(shè)置為true,僅此而已.被設(shè)置中斷標(biāo)記的線程將繼續(xù)運(yùn)行而不受影響.

    interrupt()并不能真正的中斷線程,需要被調(diào)用的線程自己進(jìn)行配合才行:

    • 在正常運(yùn)行任務(wù)時(shí),經(jīng)常檢查本線程的中斷標(biāo)志位痛阻,如果被設(shè)置了中斷標(biāo)志就自行停止線程老充。
    • 在調(diào)用阻塞方法時(shí)正確處理InterruptedException異常。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钦椭,一起剝皮案震驚了整個(gè)濱河市盈简,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌携取,老刑警劉巖攒钳,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異歹茶,居然都是意外死亡夕玩,警方通過(guò)查閱死者的電腦和手機(jī)你弦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)燎孟,“玉大人禽作,你說(shuō)我怎么就攤上這事】常” “怎么了旷偿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)爆侣。 經(jīng)常有香客問(wèn)我萍程,道長(zhǎng),這世上最難降的妖魔是什么兔仰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任茫负,我火速辦了婚禮,結(jié)果婚禮上乎赴,老公的妹妹穿的比我還像新娘忍法。我一直安慰自己,他們只是感情好榕吼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布饿序。 她就那樣靜靜地躺著,像睡著了一般羹蚣。 火紅的嫁衣襯著肌膚如雪原探。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天顽素,我揣著相機(jī)與錄音咽弦,去河邊找鬼。 笑死戈抄,一個(gè)胖子當(dāng)著我的面吹牛离唬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播划鸽,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼戚哎!你這毒婦竟也來(lái)了裸诽?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤型凳,失蹤者是張志新(化名)和其女友劉穎丈冬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體甘畅,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡埂蕊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年往弓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓄氧。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡函似,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出喉童,到底是詐尸還是另有隱情撇寞,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布堂氯,位于F島的核電站蔑担,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咽白。R本人自食惡果不足惜啤握,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晶框。 院中可真熱鬧恨统,春花似錦、人聲如沸三妈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)畴蒲。三九已至悠鞍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間模燥,已是汗流浹背咖祭。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蔫骂,地道東北人么翰。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像辽旋,于是被迫代替她去往敵國(guó)和親浩嫌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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

  • 第三章 Java內(nèi)存模型 3.1 Java內(nèi)存模型的基礎(chǔ) 通信在共享內(nèi)存的模型里补胚,通過(guò)寫(xiě)-讀內(nèi)存中的公共狀態(tài)進(jìn)行隱...
    澤毛閱讀 4,346評(píng)論 2 22
  • 下面是我自己收集整理的Java線程相關(guān)的面試題码耐,可以用它來(lái)好好準(zhǔn)備面試。 參考文檔:-《Java核心技術(shù) 卷一》-...
    阿呆變Geek閱讀 14,757評(píng)論 14 507
  • 本文主要講了java中多線程的使用方法溶其、線程同步骚腥、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法瓶逃、概述等束铭。 首先講...
    李欣陽(yáng)閱讀 2,444評(píng)論 1 15
  • 一廓块、多線程 說(shuō)明下線程的狀態(tài) java中的線程一共有 5 種狀態(tài)。 NEW:這種情況指的是契沫,通過(guò) New 關(guān)鍵字創(chuàng)...
    Java旅行者閱讀 4,662評(píng)論 0 44
  • 要不你先拿一套足貼試用带猴。用得好再來(lái)多拿兩套,我依然給你三套R(shí)M180的優(yōu)惠價(jià)格埠褪。 要不直接拿“三套R(shí)M180”優(yōu)惠...
    ShuJiun閱讀 158評(píng)論 0 0