八、死鎖

一奥喻、什么是死鎖(deadlock)偶宫?

死鎖是因為使用了加鎖機制所引發(fā)的。是指兩個或兩個以上的進程在執(zhí)行過程中环鲤,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象纯趋,若無外力作用,它們都將無法推進下去冷离,此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖吵冒。

二、死鎖的必要條件

  • 多個操作者(M>=2)操作多個資源(N>=2) M>=N
  • 爭奪資源的順序不對
    嚴(yán)格意義上來說:

1.互斥
2.不剝奪
3.請求和保持
4.循環(huán)等待

三西剥、代碼示例

/**
 *類說明:演示普通賬戶的死鎖和解決
 */
public class NormalDeadLock {
    private static Object valueFirst = new Object();//第一個鎖
    private static Object valueSecond = new Object();//第二個鎖

    //先拿第一個鎖痹栖,再拿第二個鎖
    private static void fisrtToSecond() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        synchronized (valueFirst){
            System.out.println(threadName + " 1st");
            Thread.sleep(100);
            synchronized (valueSecond){
                System.out.println(threadName + " 2nd");
            }
        }
    }

    //先拿第二個鎖,再拿第一個鎖
    private static void SecondToFisrt() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        //TODO
        synchronized (valueSecond){
            System.out.println(threadName + " 2nd");
            Thread.sleep(100);
            synchronized (valueFirst){
                System.out.println(threadName + " 1st");
            }
        }
    }

    private static class TestThread extends Thread{

        private String name;

        public TestThread(String name) {
            this.name = name;
        }

        public void run(){
            Thread.currentThread().setName(name);
            try {
                //先拿第一個鎖再拿第二個鎖
                SecondToFisrt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread.currentThread().setName("TestDeadLock");
        TestThread testThread = new TestThread("SubTestThread");
        testThread.start();
        try {
            fisrtToSecond();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

運行結(jié)果:

死鎖

可以看到瞭空,兩個線程僵持著结耀,程序沒有死,兩個線程都沒有做事匙铡,阻塞了图甜,而且沒有打印任何的異常信息。你只會感覺到程序越來越慢鳖眼。死鎖一旦發(fā)生黑毅,問題的定位是很難的。
但是我們用的idea钦讳,可以看到如下的堆棧信息:
堆棧信息

可以看到一個線程持有了一把鎖矿瘦,在等待另一個鎖。但是另一個線程持有的剛好是另一個線程需要的鎖愿卒,等待的也是另一個線程持有的鎖缚去,他們互不釋放,因此產(chǎn)生了死鎖琼开。
在生產(chǎn)環(huán)境中易结,我們可以用jdk提供的jstack命令去觀察線程的堆棧。參考:https://www.cnblogs.com/wuchanming/p/7766994.html

四柜候、解決方法

如果我們知道線程的鎖的順序搞动,直接調(diào)整鎖的順序即可,但是如果是動態(tài)的場景渣刷,我們很難去發(fā)現(xiàn)線程鎖的順序鹦肿。這種情況我們有兩種解決方法:

  • 增加一個第三個鎖,先比較第一個和第二個鎖的哈希值辅柴,如果雙方?jīng)]有比較出大小箩溃,就鎖第三個鎖瞭吃,直到兩把鎖比較出大小為止。相當(dāng)于NBA必須有輸贏
/**
 *
 *類說明:不會產(chǎn)生死鎖的安全轉(zhuǎn)賬
 * 誰的hash在前涣旨,就先鎖誰
 */
public class SafeOperate implements ITransfer {

    private static Object tieLock = new Object();//第三把鎖

    @Override
    public void transfer(UserAccount from, UserAccount to, int amount)
            throws InterruptedException {

        int fromHash = System.identityHashCode(from);
        int toHash = System.identityHashCode(to);

        if(fromHash<toHash){
            synchronized (from){
                System.out.println(Thread.currentThread().getName()+" get "+from.getName());
                Thread.sleep(100);
                synchronized (to){
                    System.out.println(Thread.currentThread().getName()+" get "+to.getName());
                    from.flyMoney(amount);
                    to.addMoney(amount);
                    System.out.println(from);
                    System.out.println(to);
                }
            }
        }else if(toHash<fromHash){
            synchronized (to){
                System.out.println(Thread.currentThread().getName()+" get"+to.getName());
                Thread.sleep(100);
                synchronized (from){
                    System.out.println(Thread.currentThread().getName()+" get"+from.getName());
                    from.flyMoney(amount);
                    to.addMoney(amount);
                    System.out.println(from);
                    System.out.println(to);
                }
            }
        }else{
            synchronized (tieLock){
                synchronized (from){
                    synchronized (to){
                        from.flyMoney(amount);
                        to.addMoney(amount);
                    }
                }
            }
        }
    }
}

  • 使用tryLock()機制
/**
 *
 *類說明:不會產(chǎn)生死鎖的安全轉(zhuǎn)賬第二種方法
 * 嘗試拿鎖
 */
public class SafeOperateToo implements ITransfer {

    @Override
    public void transfer(UserAccount from, UserAccount to, int amount)
            throws InterruptedException {
        Random r = new Random();
        while(true){
            if(from.getLock().tryLock()){
                System.out.println(Thread.currentThread().getName()
                        +" get"+from.getName());
                try{
                    if(to.getLock().tryLock()){
                        try{
                            System.out.println(Thread.currentThread().getName()
                                    +" get"+to.getName());
                            from.flyMoney(amount);
                            to.addMoney(amount);
                            System.out.println(from);
                            System.out.println(to);
                            break;
                        }finally{
                            to.getLock().unlock();
                        }
                    }
                }finally {
                    from.getLock().unlock();
                }

            }
            //為什么要休眠兩毫秒歪架?拿鎖的過程會很長,反復(fù)地拿鎖开泽,這種情況會造成CPU的浪費牡拇。
            //有A,B兩個線程魁瞪,都需要去拿c,d兩把鎖
            //A持有了c鎖穆律,同事B持有了d鎖;A想要獲取d鎖导俘,于是去嘗試峦耘,但是B想要獲取c鎖,于是也去嘗試旅薄,嘗試結(jié)束之后如果沒有獲取到鎖的話辅髓,就將自己持有的鎖釋放掉,但是釋放之后另一個需要相應(yīng)鎖的線程并不知道
            //然后接著又拿起自己的鎖去嘗試少梁。洛口。。凯沪。又去釋放第焰,造成了資源的浪費。
            //這種情況叫活鎖妨马,讓拿鎖的時機稍微錯開一點點挺举,打斷了拿鎖和釋放鎖之間的碰撞情況
            Thread.sleep(r.nextInt(2));
        }
    }
}
活鎖(為什么需要休眠兩毫秒?)

拿鎖的過程會很長烘跺,反復(fù)地拿鎖湘纵,這種情況會造成CPU的浪費。
有A,B兩個線程滤淳,都需要去拿c,d兩把鎖梧喷。A持有了c鎖,同事B持有了d鎖脖咐;A想要獲取d鎖伤柄,于是去嘗試,但是B想要獲取c鎖文搂,于是也去嘗試适刀,嘗試結(jié)束之后如果沒有獲取到鎖的話,就將自己持有的鎖釋放掉煤蹭,但是釋放之后另一個需要相應(yīng)鎖的線程并不知道笔喉。然后接著又拿起自己的鎖去嘗試......又去釋放取视,這樣一直循環(huán)下去。造成了資源的浪費常挚。
這種情況叫活鎖作谭,讓拿鎖的時機稍微錯開一點點,打斷了拿鎖和釋放鎖之間的碰撞情況

五奄毡、線程饑餓

低優(yōu)先級的線程總是拿不到執(zhí)行時間以至于這個線程一直干等著得不到執(zhí)行折欠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吼过,隨后出現(xiàn)的幾起案子锐秦,更是在濱河造成了極大的恐慌,老刑警劉巖盗忱,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酱床,死亡現(xiàn)場離奇詭異,居然都是意外死亡趟佃,警方通過查閱死者的電腦和手機扇谣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門闲昭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罐寨,“玉大人,你說我怎么就攤上這事序矩。” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵珠叔,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么淡溯? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任择懂,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己勺拣,他們只是感情好苇经,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宦言,像睡著了一般扇单。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奠旺,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天蜘澜,我揣著相機與錄音阻桅,去河邊找鬼。 笑死兼都,一個胖子當(dāng)著我的面吹牛嫂沉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扮碧,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼趟章,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了慎王?” 一聲冷哼從身側(cè)響起蚓土,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赖淤,沒想到半個月后蜀漆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡咱旱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年确丢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吐限。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鲜侥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诸典,到底是詐尸還是另有隱情描函,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布狐粱,位于F島的核電站舀寓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏肌蜻。R本人自食惡果不足惜互墓,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宋欺。 院中可真熱鬧轰豆,春花似錦、人聲如沸齿诞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祷杈。三九已至斑司,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宿刮。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工互站, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人僵缺。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓胡桃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親磕潮。 傳聞我的和親對象是個殘疾皇子翠胰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

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

  • 死鎖產(chǎn)生的原因和解鎖的方法 產(chǎn)生死鎖的四個必要條件: (1) 互斥條件:一個資源每次只能被一個進程使用。 (2) ...
    憩在河岸上的魚丶閱讀 1,483評論 0 4
  • 產(chǎn)生死鎖的四個必要條件: (1) 互斥條件:一個資源每次只能被一個進程使用自脯。 (2) 請求與保持條件:一個進程因請...
    像敏銳的狗閱讀 977評論 0 0
  • 第11章:死鎖和進程通信 死鎖概念 死鎖處理方法 死鎖預(yù)防(Deadlock Prevention) 死鎖避免(D...
    liuzhangjie閱讀 530評論 0 0
  • 國慶假期的第四天,和計劃一樣焕参,每天宅在家中轻纪,讀書,運動龟糕,睡覺桐磁。 每天都過得十分規(guī)律悔耘,早起讀書讲岁,中午游泳,下午補...
    答案_3a83閱讀 114評論 0 1
  • 看著各種日常用到的軟件衬以,網(wǎng)站固逗,總有這樣那樣的不喜歡令漂,也申請過免費空間放點收藏和工具,但畢竟都是免費的,沒多久就都沒...
    FinTech閱讀 331評論 0 0