Java錯(cuò)誤的停止線(xiàn)程的方法

1. 被棄用的 stop弱卡、suspend 和 resume 方法

stop() 來(lái)停止線(xiàn)程画株,會(huì)導(dǎo)致線(xiàn)程運(yùn)行一半突然停止谦铃,沒(méi)辦法完成一個(gè)基本單位的操作,會(huì)造成臟數(shù)據(jù)赞厕;
模擬連隊(duì)發(fā)裝備代碼示例:

public class StopThread implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("連隊(duì)" + i + " 開(kāi)始領(lǐng)妊藓:");
            for (int j = 1; j <= 10; j++) {
                System.out.println("士兵" + j + " 開(kāi)始領(lǐng)取");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("====連隊(duì)" + i + "領(lǐng)取完畢====");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThread());
        thread.start();
        // 等待666毫秒,停止線(xiàn)程
        Thread.sleep(666);
        thread.stop();
    }
}

運(yùn)行結(jié)果:

連隊(duì)1 開(kāi)始領(lǐng)让笊!:
士兵1 開(kāi)始領(lǐng)取
士兵2 開(kāi)始領(lǐng)取
士兵3 開(kāi)始領(lǐng)取
士兵4 開(kāi)始領(lǐng)取
士兵5 開(kāi)始領(lǐng)取
士兵6 開(kāi)始領(lǐng)取
士兵7 開(kāi)始領(lǐng)取
士兵8 開(kāi)始領(lǐng)取
士兵9 開(kāi)始領(lǐng)取
士兵10 開(kāi)始領(lǐng)取
====連隊(duì)1領(lǐng)取完畢====
連隊(duì)2 開(kāi)始領(lǐng)冉┏邸:
士兵1 開(kāi)始領(lǐng)取
士兵2 開(kāi)始領(lǐng)取
士兵3 開(kāi)始領(lǐng)取

Process finished with exit code 0

會(huì)發(fā)現(xiàn) 連隊(duì)2 才3個(gè)人領(lǐng)完,其他人都還沒(méi)有領(lǐng)到唁毒,線(xiàn)程突然就結(jié)束了,這樣就會(huì)造成數(shù)據(jù)的錯(cuò)亂星爪!
并且這種數(shù)據(jù)的錯(cuò)亂后期難以排查浆西!
Oracle官方文檔對(duì)于為什么Thread.stop不推薦使用的解釋
https://docs.oracle.com/javase/7/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

image.png

suspend和resume被拋棄原因:
suspend和stop不一樣,suspend不會(huì)破壞對(duì)象顽腾,但是它會(huì)讓一個(gè)線(xiàn)程掛起近零,在恢復(fù)之前,鎖不會(huì)釋放抄肖,也就是它是帶著鎖去進(jìn)行休息的久信,這樣的話(huà)很容易造成死鎖。
resume

2. 用volatile設(shè)置boolean標(biāo)記位

2.1:看似可行的代碼

public class WrongWayVolatile implements Runnable {

    /**
     * 設(shè)置boolean類(lèi)型的標(biāo)記位
     */
    private volatile boolean canceled = false;

    public static void main(String[] args) throws InterruptedException {
        WrongWayVolatile r = new WrongWayVolatile();
        Thread t = new Thread(r);
        t.start();

        // 等待5秒后
        Thread.sleep(5000);
        // 更改canceled值以達(dá)到停止程序的目的
        r.canceled = true;
    }

    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= Integer.MAX_VALUE && !canceled) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍數(shù)");
                }
                num++;
                Thread.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

程序在運(yùn)行5秒鐘后停止了漓摩,達(dá)到了預(yù)期的效果裙士。

2.2:當(dāng)陷入阻塞時(shí),無(wú)法停止
下面代碼示例中管毙,生產(chǎn)者的生產(chǎn)速度很快腿椎,消費(fèi)者消費(fèi)速度慢,
所以阻塞隊(duì)列滿(mǎn)了以后夭咬,生產(chǎn)者會(huì)阻塞停止生產(chǎn)啃炸,等待消費(fèi)者進(jìn)一步消費(fèi)

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class WrongWayVolatileCantStop {

    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
        Producer producer = new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        // 等待1秒讓生產(chǎn)者將隊(duì)列填滿(mǎn)
        Thread.sleep(1000);

        Consumer consumer = new Consumer(storage);
        while (consumer.needMoreNums()) {
            System.out.println(consumer.storage.take() + "被消費(fèi)了!");
            // 消費(fèi)是需要時(shí)間的卓舵,設(shè)置個(gè)100毫秒
            Thread.sleep(100);
        }
        System.out.println("消費(fèi)者不需要更多數(shù)據(jù)了南用。");

        // 一旦消費(fèi)者不需要更多數(shù)據(jù)了,則應(yīng)該讓生產(chǎn)者停下來(lái)掏湾,
        // 將標(biāo)記位設(shè)置為true裹虫,看是否能將線(xiàn)程停止?
        producer.canceled = true;
        System.out.println(producer.canceled);
    }

}

/**
 * 生產(chǎn)者
 */
class Producer implements Runnable {

    /**
     * 設(shè)置boolean類(lèi)型的標(biāo)記位
     */
    public volatile boolean canceled = false;

    BlockingQueue storage;

    public Producer(BlockingQueue storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= Integer.MAX_VALUE && !canceled) {
                if (num % 100 == 0) {
                    storage.put(num);
                    System.out.println(num + "是100的倍數(shù)融击,被放到了倉(cāng)庫(kù)中恒界。");
                }
                num++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("生產(chǎn)者結(jié)束運(yùn)行");
        }
    }
}

/**
 * 消費(fèi)者
 */
class Consumer {
    BlockingQueue storage;

    public Consumer(BlockingQueue storage) {
        this.storage = storage;
    }

    public boolean needMoreNums() {
        // 隨機(jī)返回true或false
        if (Math.random() > 0.95) {
            return false;
        }
        return true;
    }
}

最后的運(yùn)行結(jié)果并沒(méi)有打印出 “生產(chǎn)者結(jié)束運(yùn)行”,
而且程序也沒(méi)有停止Q庾臁Jā涩拙!

程序沒(méi)有停止

為什么?
因?yàn)樯a(chǎn)者暫停生產(chǎn)時(shí)耸采,是阻塞在 storage.put(num);
而且也沒(méi)有人去喚醒兴泥,所以 while() 條件也無(wú)法執(zhí)行,也不知道 canceled 的值已經(jīng)被改變虾宇!
只會(huì)一直等在 storage.put(num); 這里搓彻。
解決方法:使用 interrupt

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class WrongWayVolatileCantStop {

    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
        Producer producer = new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        // 等待1秒讓生產(chǎn)者將隊(duì)列填滿(mǎn)
        Thread.sleep(1000);

        Consumer consumer = new Consumer(storage);
        while (consumer.needMoreNums()) {
            System.out.println(consumer.storage.take() + "被消費(fèi)了!");
            // 消費(fèi)是需要時(shí)間的嘱朽,設(shè)置個(gè)100毫秒
            Thread.sleep(100);
        }
        System.out.println("消費(fèi)者不需要更多數(shù)據(jù)了旭贬。");

        // 一旦消費(fèi)者不需要更多數(shù)據(jù)了,則應(yīng)該讓生產(chǎn)者停下來(lái)搪泳,
        // 使用interrupt通知停止線(xiàn)程
        producerThread.interrupt();
    }

}

/**
 * 生產(chǎn)者
 */
class Producer implements Runnable {
    BlockingQueue storage;

    public Producer(BlockingQueue storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= Integer.MAX_VALUE && !Thread.currentThread().isInterrupted()) {
                if (num % 100 == 0) {
                    storage.put(num);
                    System.out.println(num + "是100的倍數(shù)稀轨,被放到了倉(cāng)庫(kù)中。");
                }
                num++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("生產(chǎn)者結(jié)束運(yùn)行");
        }
    }
}

/**
 * 消費(fèi)者
 */
class Consumer {
    BlockingQueue storage;

    public Consumer(BlockingQueue storage) {
        this.storage = storage;
    }

    public boolean needMoreNums() {
        // 隨機(jī)返回true或false
        if (Math.random() > 0.95) {
            return false;
        }
        return true;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末岸军,一起剝皮案震驚了整個(gè)濱河市奋刽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌艰赞,老刑警劉巖佣谐,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異方妖,居然都是意外死亡狭魂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)党觅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)趁蕊,“玉大人,你說(shuō)我怎么就攤上這事仔役≈阑铮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵又兵,是天一觀(guān)的道長(zhǎng)任柜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)沛厨,這世上最難降的妖魔是什么宙地? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮逆皮,結(jié)果婚禮上宅粥,老公的妹妹穿的比我還像新娘。我一直安慰自己电谣,他們只是感情好秽梅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布抹蚀。 她就那樣靜靜地躺著,像睡著了一般企垦。 火紅的嫁衣襯著肌膚如雪环壤。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,208評(píng)論 1 299
  • 那天钞诡,我揣著相機(jī)與錄音郑现,去河邊找鬼。 笑死荧降,一個(gè)胖子當(dāng)著我的面吹牛接箫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播朵诫,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼辛友,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了拗窃?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泌辫,失蹤者是張志新(化名)和其女友劉穎随夸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體震放,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宾毒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了殿遂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诈铛。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖墨礁,靈堂內(nèi)的尸體忽然破棺而出幢竹,到底是詐尸還是另有隱情,我是刑警寧澤恩静,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布焕毫,位于F島的核電站,受9級(jí)特大地震影響驶乾,放射性物質(zhì)發(fā)生泄漏邑飒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一级乐、第九天 我趴在偏房一處隱蔽的房頂上張望疙咸。 院中可真熱鬧,春花似錦风科、人聲如沸撒轮。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)腔召。三九已至杆查,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間臀蛛,已是汗流浹背亲桦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浊仆,地道東北人客峭。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像抡柿,于是被迫代替她去往敵國(guó)和親舔琅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354