Java - Thread 的 Deadlock

By sunwc 2023-03-23 Java

什麼是死鎖?

當不同執行緒分別占用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了執行緒的死鎖

死鎖出現後的現象?

出現死鎖後,不會出現例外,也不會有提示,只是所有執行緒都處於阻塞的狀態;因此我們在使用 synchronized 的時候,要避免死鎖的情況發生

如何避免死鎖?

  • 盡量減少同步資源的宣告,例如 有 static 關鍵字
  • 避免嵌套synchronized(同步鎖){}

例子

/**
 * 演示執行緒死鎖問題
 * @author sunwc
 * @create 2023-03-24 上午 10:49
 */
public class DeadlockTest {

    public static void main(String[] args) {

        // 1. 第一個同步鎖
        StringBuffer s1 = new StringBuffer();
        // 2. 第二個同步鎖
        StringBuffer s2 = new StringBuffer();

        // 3.匿名子類創建執行緒(子Thread1)
        new Thread() {
            @Override
            public void run() {

                synchronized (s1) {
                    s1.append("1");
                    s2.append("a");

                    // 5. 阻塞0.1秒,增加死鎖概率
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s2) {
                        s1.append("2");
                        s2.append("b");

                        Thread.currentThread().setName("子Thread1");
                        System.out.println(Thread.currentThread().getName() + " : " + s1);
                        System.out.println(Thread.currentThread().getName() + " : " + s2);
                    }
                }
            }
        }.start();

        // 4. 匿名實現類創建執行緒
        new Thread(new Runnable() {
            @Override
            public void run() {

                synchronized (s2) {
                    s1.append("3");
                    s2.append("c");

                    // 6. 阻塞0.1秒,增加死鎖概率
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s1) {
                        s1.append("4");
                        s2.append("d");

                        Thread.currentThread().setName("子Thread2");
                        System.out.println(Thread.currentThread().getName() + " : " + s1);
                        System.out.println(Thread.currentThread().getName() + " : " + s2);
                    }
                }
            }
        }).start();
    }
}

總結

本文使用一個例子演示了兩個執行緒如何剛好都在等對方持有的那把鎖,而導致程式整個卡住了,因此使用 synchronized 機制時,還是要思考一下如何避免死鎖的情況產生,就像我們使用迴圈也會避免出現死循環一樣