一個(gè)死鎖的例子、死鎖的條件、如何避免死鎖

一、什么是死鎖

死鎖不僅在個(gè)人學(xué)習(xí)中,甚至在開(kāi)發(fā)中也并不常見(jiàn)。但是一旦出現(xiàn)死鎖,后果將非常嚴(yán)重。
首先什么是死鎖呢?打個(gè)比方,就好像有兩個(gè)人打架,互相限制住了(鎖住,抱住)彼此一樣,互相動(dòng)彈不得,而且互相歐氣,你不松手我就不松手。好了誰(shuí)也動(dòng)彈不得。
在多線程的環(huán)境下,勢(shì)必會(huì)對(duì)資源進(jìn)行搶奪。當(dāng)兩個(gè)線程鎖住了當(dāng)前資源,但都需要對(duì)方的資源才能進(jìn)行下一步操作,這個(gè)時(shí)候兩方就會(huì)一直等待對(duì)方的資源釋放。這就形成了死鎖。這些永遠(yuǎn)在互相等待的進(jìn)程稱(chēng)為死鎖進(jìn)程。

那么我們來(lái)總結(jié)一下死鎖產(chǎn)生的條件:

互斥:資源的鎖是排他性的,加鎖期間只能有一個(gè)線程擁有該資源。其他線程只能等待鎖釋放才能?chē)L試獲取該資源。
請(qǐng)求和保持:當(dāng)前線程已經(jīng)擁有至少一個(gè)資源,但其同時(shí)又發(fā)出新的資源請(qǐng)求,而被請(qǐng)求的資源被其他線程擁有。此時(shí)進(jìn)入保持當(dāng)前資源并等待下個(gè)資源的狀態(tài)。
不剝奪:線程已擁有的資源,只能由自己釋放,不能被其他線程剝奪。
循環(huán)等待:是指有多個(gè)線程互相的請(qǐng)求對(duì)方的資源,但同時(shí)擁有對(duì)方下一步所需的資源。形成一種循環(huán),類(lèi)似2)請(qǐng)求和保持。但此處指多個(gè)線程的關(guān)系。并不是指單個(gè)線程一直在循環(huán)中等待。
什么?還是不理解?那我們直接上代碼,動(dòng)手寫(xiě)一個(gè)死鎖。

二、寫(xiě)一個(gè)死鎖

根據(jù)條件,我們讓兩個(gè)線程互相請(qǐng)求保持。

/**
 * 模擬死鎖場(chǎng)景
 */
public class DeadLockDemo implements Runnable{

    public static int flag = 1;

    //static 變量是 類(lèi)對(duì)象共享的
    static Object o1 = new Object();
    static Object o2 = new Object();

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":此時(shí) flag = " + flag);
        if(flag == 1){
            synchronized (o1){
                try {
                    System.out.println("我是" + Thread.currentThread().getName() + "鎖住 o1");
                    Thread.sleep(3000);
                    System.out.println(Thread.currentThread().getName() + "醒來(lái)->準(zhǔn)備獲取 o2");
                }catch (Exception e){
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println(Thread.currentThread().getName() + "拿到 o2");//第24行
                }
            }
        }
        if(flag == 0){
            synchronized (o2){
                try {
                    System.out.println("我是" + Thread.currentThread().getName() + "鎖住 o2");
                    Thread.sleep(3000);
                    System.out.println(Thread.currentThread().getName() + "醒來(lái)->準(zhǔn)備獲取 o1");
                }catch (Exception e){
                    e.printStackTrace();
                }
                synchronized (o1){
                    System.out.println(Thread.currentThread().getName() + "拿到 o1");//第38行
                }
            }
        }
    }

    public static  void main(String args[]){

        DeadLockDemo t1 = new DeadLockDemo();
        DeadLockDemo t2 = new DeadLockDemo();
        t1.flag = 1;
        new Thread(t1).start();

        //讓main線程休眠1秒鐘,保證t2開(kāi)啟鎖住o2.進(jìn)入死鎖
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t2.flag = 0;
        new Thread(t2).start();
    }

代碼中,
t1創(chuàng)建,t1先拿到o1的鎖,開(kāi)始休眠3秒。然后
t2線程創(chuàng)建,t2拿到o2的鎖,開(kāi)始休眠3秒。然后
t1先醒來(lái),準(zhǔn)備拿o2的鎖,發(fā)現(xiàn)o2已經(jīng)加鎖,只能等待o2的鎖釋放。
t2后醒來(lái),準(zhǔn)備拿o1的鎖,發(fā)現(xiàn)o1已經(jīng)加鎖,只能等待o1的鎖釋放。
t1,t2形成死鎖。

我們查看運(yùn)行狀態(tài)


image.png

三、發(fā)現(xiàn)排查死鎖情況

我們利用jdk提供的工具定位死鎖問(wèn)題:

jps顯示所有當(dāng)前Java虛擬機(jī)進(jìn)程名及pid.
jstack打印進(jìn)程堆棧信息。


image.png

列出所有java進(jìn)程。
我們檢查一下DeadLockDemo,為什么這個(gè)線程不退棧。

jstack 11170
image.png

我們直接翻到最后:已經(jīng)檢測(cè)出了一個(gè)java級(jí)別死鎖。其中兩個(gè)線程分別卡在了代碼第27行和第41行。檢查我們代碼的對(duì)應(yīng)位置,即可排查錯(cuò)誤。此處我們是第二個(gè)鎖始終拿不到,所以死鎖了。

四、解決辦法

死鎖一旦發(fā)生,我們就無(wú)法解決了。所以我們只能避免死鎖的發(fā)生。
既然死鎖需要滿足四種條件,那我們就從條件下手,只要打破任意規(guī)則即可。

(互斥)盡量少用互斥鎖,能加讀鎖,不加寫(xiě)鎖。當(dāng)然這條無(wú)法避免。
(請(qǐng)求和保持)采用資源靜態(tài)分配策略(進(jìn)程資源靜態(tài)分配方式是指一個(gè)進(jìn)程在建立時(shí)就分配了它需要的全部資源).我們盡量不讓線程同時(shí)去請(qǐng)求多個(gè)鎖,或者在擁有一個(gè)鎖又請(qǐng)求不到下個(gè)鎖時(shí),不保持等待,先釋放資源等待一段時(shí)間在重新請(qǐng)求。
(不剝奪)允許進(jìn)程剝奪使用其他進(jìn)程占有的資源。優(yōu)先級(jí)。
(循環(huán)等待)盡量調(diào)整獲得鎖的順序,不發(fā)生嵌套資源請(qǐng)求。加入超時(shí)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容