1. 程式人生 > 程式設計 >史上最通俗理解的Java死鎖程式碼演示

史上最通俗理解的Java死鎖程式碼演示

死鎖的概念

知識儲備

物件鎖:Java一切皆物件,每個類都有一個class檔案。由class檔案可以new出物件,我們簡單認識 下java物件,物件有個物件頭資訊,也就是這個物件概述,其中一條資訊就是物件鎖,也就是我們當前物件有沒有被鎖定,被哪個引用鎖定。

synchronized:synchronized是java關鍵詞,如果運用到方法上代表我們鎖的是這個方法,如果我們鎖的程式碼塊,代表再這個程式碼塊內我們持有這個鎖,Java Effective也是提倡減小鎖的範圍。我們進入同步程式碼塊會加鎖,執行完同步程式碼塊會釋放鎖。

死鎖:通俗理解為死掉的鎖。如果沒有死掉的鎖它的宣告週期是:持有鎖->釋放鎖。死後我們可以理解為持有鎖但是不釋放鎖,也就是我們同步程式碼塊沒有執行完?我們只需要分析同步程式碼塊的哪裡沒有執行就好了,看下面一個例子

演示死鎖

package com.yang.kuangTeacher;

import java.util.concurrent.TimeUnit;

/**
 * @author: fudy
 * @date: 2020/9/13 下午 12:21
 * @Decription: 演示死鎖(內容參考B站狂神說JAVA)
 **/
public class DeadLock {

  public static void main(String[] args) {
    MarkUp markUp0 = new MarkUp("迪麗熱巴",0);
    MarkUp markUp1 = new MarkUp("楊冪",1);
    markUp0.start();
    markUp1.start();
  }
}

// 口紅類
class LipStick {
}

// 鏡子類
class Mirror {
}

// 化妝類
class MarkUp extends Thread {

  private int choice;
  private String userName;

  private static LipStick lipStick = new LipStick();
  private static Mirror mirror = new Mirror();

  MarkUp(String userName,int choice) {
    this.userName = userName;
    this.choice = choice;
  }

  @Override
  public void run() {
    try {
      markUP();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  private void markUP() throws InterruptedException {
    // 如果選擇0方式化妝
    if (choice == 0) {
      // 同步程式碼塊的鎖,在同步程式碼塊有效
      synchronized (lipStick) {
        System.out.println(userName + "拿到了口紅");
        // 拿到口紅後再拿鏡子
        TimeUnit.SECONDS.sleep(1);
        // 程式執行此處會停止 		 -----------------這裡死鎖----------------------->
        synchronized (mirror) {
          System.out.println(userName + "拿到了鏡子");
        }
      }
    }
    // 如果選擇1方式化妝
    if (choice == 1) {
      // 同步程式碼塊的鎖,在同步程式碼塊有效
      synchronized (mirror) {
        System.out.println(userName + "拿到了鏡子");
        // 拿到鏡子後再拿口紅
        TimeUnit.SECONDS.sleep(1);
        // 程式執行此處會停止     -----------------這裡死鎖----------------------->
        synchronized (lipStick) {
          System.out.println(userName + "拿到了口紅");
        }
      }
    }
  }
}

我們剛才認為,死鎖是由於同步程式碼塊沒有執行完,導致不會釋放鎖,我們分析以上兩個死鎖的原因。

  • 線上程1方式0化妝中由於我們拿到了口紅鎖後,睡眠一秒鐘(有可能先執行執行緒2)
  • 線上程2方式1化妝中由於我們拿到了鏡子鎖後,睡眠一秒鐘

假如執行緒1先獲得口紅鎖執行完畢,準備拿鏡子鎖時,發現鏡子物件被持有了,所以他會等待鏡子鎖被釋放。

執行緒2先執行獲得鏡子鎖完畢,準備拿口紅鎖時,發現口紅物件被持有了,所以他會等待口紅鎖被釋放。

如果我們不關閉程式,兩個執行緒會一直等待下去。我們可以理解為死鎖,無法釋放鎖。

解決死鎖

在上述例子中,我們因為想同時拿到兩個鎖去做一件事情才會導致死鎖,按照Java Effective提倡減小鎖的範圍,我們對問題進行改進。

我們可以拿到口紅鎖後執行口紅方法後釋放口紅鎖,想要鏡子鎖再同步程式碼塊拿鏡子鎖即可。

package com.yang.kuangTeacher;

import java.util.concurrent.TimeUnit;

/**
 * @author: fudy
 * @date: 2020/9/13 下午 12:21
 * @Decription: 演示死鎖(內容參考B站狂神說JAVA)
 **/
public class DeadLock {

  public static void main(String[] args) {
    MarkUp markUp0 = new MarkUp("迪麗熱巴",int choice) {
    this.userName = userName;
    this.choice = choice;
  }

  @Override
  public void run() {
    try {
      markUP();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  private void markUP() throws InterruptedException {
    // 如果選擇0方式化妝
    if (choice == 0) {
      // 同步程式碼塊的鎖,在同步程式碼塊有效
      synchronized (lipStick) {
        System.out.println(userName + "拿到了口紅");
        TimeUnit.SECONDS.sleep(1);
      }
      // 拿到口紅後再拿鏡子      ------------------------改進---------------------------
      synchronized (mirror) {
        System.out.println(userName + "拿到了鏡子");
      }
    }
    // 如果選擇1方式化妝
    if (choice == 1) {
      // 同步程式碼塊的鎖,在同步程式碼塊有效
      synchronized (mirror) {
        System.out.println(userName + "拿到了鏡子");
        TimeUnit.SECONDS.sleep(1);
      }
      // 拿到鏡子後再拿口紅      ------------------------改進---------------------------
      synchronized (lipStick) {
        System.out.println(userName + "拿到了口紅");
      }
    }
  }
}

通過及時釋放鎖,也就是縮小同步程式碼塊的範圍,我們使用鎖結束後及時釋放,這樣是一種解決死鎖的方式,通過這個例子我們以後編寫程式碼就會警惕鎖的同步程式碼的範圍。

總結

到此這篇關於Java死鎖程式碼演示的文章就介紹到這了,更多相關Java死鎖程式碼演示內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!