最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java并發(fā)常見問題之死鎖/活鎖/饑餓的排查與解決方法

 更新時間:2026年03月05日 10:02:53   作者:C雨后彩虹  
Java死鎖問題是多線程編程中一個經(jīng)典且極具挑戰(zhàn)性的并發(fā)控制問題,它直接影響程序的穩(wěn)定性、性能和可維護性,這篇文章主要介紹了Java并發(fā)常見問題之死鎖/活鎖/饑餓的排查與解決方法的相關(guān)資料,需要的朋友可以參考下

一、前言

在多線程并發(fā)編程中,除了數(shù)據(jù)安全問題,線程協(xié)作異常是另一類高頻問題,其中死鎖、活鎖、饑餓是最典型的三類問題。這些問題會導致線程無法正常執(zhí)行、系統(tǒng)性能下降甚至服務不可用,且排查難度高 —— 死鎖可能隱藏數(shù)月,在高并發(fā)場景下才會觸發(fā)。

本文將深入剖析這三類問題的產(chǎn)生原因、典型場景、排查方法,并提供可落地的解決方案與避坑指南。

二、死鎖(Deadlock)

1. 死鎖的定義

死鎖是指兩個或多個線程互相持有對方所需的鎖,且都不釋放自己持有的鎖,導致所有線程永久阻塞,無法繼續(xù)執(zhí)行的狀態(tài)。

2. 死鎖的四大必要條件(缺一不可)

只有同時滿足以下 4 個條件,才會產(chǎn)生死鎖:

  1. 互斥條件:鎖資源只能被一個線程持有,其他線程無法獲取;

  2. 持有并等待條件:線程持有已獲取的鎖,同時等待其他線程持有的鎖;

  3. 不可剝奪條件:線程持有的鎖不能被強制剝奪,只能由線程主動釋放;

  4. 循環(huán)等待條件:線程 A 等待線程 B 的鎖,線程 B 等待線程 A 的鎖,形成循環(huán)等待鏈。

3. 死鎖典型案例

/**
 * 死鎖演示:線程1持有鎖A,等待鎖B;線程2持有鎖B,等待鎖A
 */
public class DeadlockDemo {
    // 定義兩個鎖對象
    private static final Object LOCK_A = new Object();
    private static final Object LOCK_B = new Object();
    public static void main(String[] args) {
        // 線程1:獲取LOCK_A → 等待LOCK_B
        new Thread(() -> {
            synchronized (LOCK_A) {
                System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_A,等待LOCK_B");
                try {
                    Thread.sleep(1000); // 放大死鎖概率
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (LOCK_B) {
                    System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_B,執(zhí)行完成");
                }
            }
        }, "線程1").start();
        // 線程2:獲取LOCK_B → 等待LOCK_A
        new Thread(() -> {
            synchronized (LOCK_B) {
                System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_B,等待LOCK_A");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (LOCK_A) {
                    System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_A,執(zhí)行完成");
                }
            }
        }, "線程2").start();
    }
}

運行結(jié)果 :兩個線程互相等待對方的鎖,永久阻塞,無后續(xù)輸出。

4. 死鎖的排查方法

方法 1:jstack 命令(最常用)

1.執(zhí)行 jps 命令,獲取進程 ID:

jps
# 輸出示例:1234 DeadlockDemo

2.執(zhí)行 jstack <進程ID> ,查看線程狀態(tài):

jstack 1234

3.關(guān)鍵輸出(死鎖提示):

Found one Java-level deadlock:
=============================
"線程2":
  waiting to lock monitor 0x00007f9e3c006800 (object 0x000000076ab60eb0, a java.lang.Object),
  which is held by "線程1"
"線程1":
  waiting to lock monitor 0x00007f9e3c009000 (object 0x000000076ab60ec0, a java.lang.Object),
  which is held by "線程2"

方法 2:JConsole 可視化工具

  1. 啟動 JConsole(JDK/bin 目錄下),連接目標進程;

  2. 切換到「線程」標簽頁,點擊「檢測死鎖」,自動識別死鎖線程及鎖信息。

5. 死鎖的解決方案

核心思路: 破壞死鎖的四大必要條件之一 ,常用方案:

方案 1:統(tǒng)一鎖獲取順序(破壞循環(huán)等待條件)

所有線程按相同的順序獲取鎖,避免循環(huán)等待:

// 優(yōu)化后:線程1和線程2都先獲取LOCK_A,再獲取LOCK_B
new Thread(() -> {
    synchronized (LOCK_A) {
        System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_A,等待LOCK_B");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (LOCK_B) {
            System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_B,執(zhí)行完成");
        }
    }
}, "線程1").start();
new Thread(() -> {
    synchronized (LOCK_A) { // 統(tǒng)一先獲取LOCK_A
        System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_A,等待LOCK_B");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (LOCK_B) {
            System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_B,執(zhí)行完成");
        }
    }
}, "線程2").start();

方案 2:使用可中斷鎖(破壞不可剝奪條件)

使用 ReentrantLock 的 lockInterruptibly() 方法,允許線程被中斷,主動釋放鎖:

import java.util.concurrent.locks.ReentrantLock;
public class DeadlockResolveByInterrupt {
    private static final ReentrantLock LOCK_A = new ReentrantLock();
    private static final ReentrantLock LOCK_B = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            try {
                // 可中斷的鎖獲取
                LOCK_A.lockInterruptibly();
                System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_A,等待LOCK_B");
                Thread.sleep(1000);
                LOCK_B.lockInterruptibly();
                System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_B,執(zhí)行完成");
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + " 被中斷,釋放鎖");
                if (LOCK_A.isHeldByCurrentThread()) {
                    LOCK_A.unlock();
                }
                if (LOCK_B.isHeldByCurrentThread()) {
                    LOCK_B.unlock();
                }
            }
        }, "線程1");
        Thread thread2 = new Thread(() -> {
            try {
                LOCK_B.lockInterruptibly();
                System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_B,等待LOCK_A");
                Thread.sleep(1000);
                LOCK_A.lockInterruptibly();
                System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_A,執(zhí)行完成");
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + " 被中斷,釋放鎖");
                if (LOCK_A.isHeldByCurrentThread()) {
                    LOCK_A.unlock();
                }
                if (LOCK_B.isHeldByCurrentThread()) {
                    LOCK_B.unlock();
                }
            }
        }, "線程2");
        thread1.start();
        thread2.start();
        // 等待3秒,若檢測到死鎖,中斷線程1
        Thread.sleep(3000);
        if (thread1.isAlive() && thread2.isAlive()) {
            thread1.interrupt();
            System.out.println("檢測到死鎖,中斷線程1");
        }
    }
}

方案 3:使用超時獲取鎖(破壞持有并等待條件)

使用 ReentrantLock 的 tryLock(long time, TimeUnit unit) 方法,超時未獲取鎖則放棄,避免永久等待:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockResolveByTimeout {
    private static final ReentrantLock LOCK_A = new ReentrantLock();
    private static final ReentrantLock LOCK_B = new ReentrantLock();
    public static void main(String[] args) {
        new Thread(() -> {
            try {
                if (LOCK_A.tryLock(2, TimeUnit.SECONDS)) { // 超時2秒
                    System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_A,等待LOCK_B");
                    Thread.sleep(1000);
                    if (LOCK_B.tryLock(2, TimeUnit.SECONDS)) {
                        System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_B,執(zhí)行完成");
                        LOCK_B.unlock();
                    } else {
                        System.out.println(Thread.currentThread().getName() + " 超時未獲取LOCK_B,釋放LOCK_A");
                        LOCK_A.unlock();
                    }
                    LOCK_A.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "線程1").start();
        new Thread(() -> {
            try {
                if (LOCK_B.tryLock(2, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_B,等待LOCK_A");
                    Thread.sleep(1000);
                    if (LOCK_A.tryLock(2, TimeUnit.SECONDS)) {
                        System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_A,執(zhí)行完成");
                        LOCK_A.unlock();
                    } else {
                        System.out.println(Thread.currentThread().getName() + " 超時未獲取LOCK_A,釋放LOCK_B");
                        LOCK_B.unlock();
                    }
                    LOCK_B.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "線程2").start();
    }
}

三、活鎖(Livelock)

1. 活鎖的定義

活鎖是指線程沒有阻塞,但因互相謙讓(或重試),導致始終無法獲取所需資源,程序無法推進的狀態(tài)。與死鎖的核心區(qū)別:死鎖是線程完全阻塞,活鎖是線程一直在執(zhí)行,但無實際進展。

2. 活鎖典型案例

/**
 * 活鎖演示:兩個線程互相釋放鎖,重試獲取對方的鎖,始終無法執(zhí)行完成
 */
public class LivelockDemo {
    private static final ReentrantLock LOCK_A = new ReentrantLock();
    private static final ReentrantLock LOCK_B = new ReentrantLock();
    public static void main(String[] args) {
        // 線程1:獲取LOCK_A失敗 → 釋放LOCK_B(若持有)→ 重試
        new Thread(() -> {
            while (true) {
                try {
                    if (LOCK_A.tryLock(100, TimeUnit.MILLISECONDS)) {
                        System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_A,嘗試獲取LOCK_B");
                        if (LOCK_B.tryLock(100, TimeUnit.MILLISECONDS)) {
                            System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_B,執(zhí)行完成");
                            LOCK_B.unlock();
                            LOCK_A.unlock();
                            break; // 執(zhí)行完成,退出循環(huán)
                        } else {
                            System.out.println(Thread.currentThread().getName() + " 獲取LOCK_B失敗,釋放LOCK_A");
                            LOCK_A.unlock();
                            Thread.sleep(100); // 謙讓,重試
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "線程1").start();
        // 線程2:獲取LOCK_B失敗 → 釋放LOCK_A(若持有)→ 重試
        new Thread(() -> {
            while (true) {
                try {
                    if (LOCK_B.tryLock(100, TimeUnit.MILLISECONDS)) {
                        System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_B,嘗試獲取LOCK_A");
                        if (LOCK_A.tryLock(100, TimeUnit.MILLISECONDS)) {
                            System.out.println(Thread.currentThread().getName() + " 獲取到LOCK_A,執(zhí)行完成");
                            LOCK_A.unlock();
                            LOCK_B.unlock();
                            break;
                        } else {
                            System.out.println(Thread.currentThread().getName() + " 獲取LOCK_A失敗,釋放LOCK_B");
                            LOCK_B.unlock();
                            Thread.sleep(100); // 謙讓,重試
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "線程2").start();
    }
}

運行結(jié)果 :兩個線程反復獲取鎖、釋放鎖、重試,始終無法同時獲取兩個鎖,陷入無限循環(huán)。

3. 活鎖的解決方案

核心思路: 打破 “同步重試” 的循環(huán) ,常用方案:

1.隨機重試延遲:每個線程重試時使用隨機的休眠時間,避免同步謙讓;

// 替換固定休眠時間為隨機時間
Thread.sleep(new Random().nextInt(500));

2.優(yōu)先級機制:為線程設置不同的優(yōu)先級,讓部分線程優(yōu)先獲取資源;

3.限制重試次數(shù):設置最大重試次數(shù),超過次數(shù)則放棄并報警,避免無限循環(huán)。

四、饑餓(Starvation)

1. 饑餓的定義

饑餓是指某些線程因優(yōu)先級低、或始終競爭不到鎖資源,導致長期無法執(zhí)行的狀態(tài)。例如:高優(yōu)先級線程持續(xù)占用 CPU,低優(yōu)先級線程始終無法執(zhí)行;非公平鎖下,某些線程始終搶不到鎖。

2. 饑餓典型案例

/**
 * 饑餓演示:高優(yōu)先級線程持續(xù)占用鎖,低優(yōu)先級線程長期無法獲取鎖
 */
public class StarvationDemo {
    private static final Object LOCK = new Object();
    public static void main(String[] args) {
        // 低優(yōu)先級線程
        Thread lowPriorityThread = new Thread(() -> {
            int count = 0;
            while (true) {
                synchronized (LOCK) {
                    System.out.println(Thread.currentThread().getName() + " 執(zhí)行第" + (++count) + "次");
                    try {
                        Thread.sleep(100); // 持有鎖時間短,但仍被高優(yōu)先級線程搶占
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "低優(yōu)先級線程");
        lowPriorityThread.setPriority(Thread.MIN_PRIORITY); // 優(yōu)先級1
        // 高優(yōu)先級線程
        Thread highPriorityThread = new Thread(() -> {
            int count = 0;
            while (true) {
                synchronized (LOCK) {
                    System.out.println(Thread.currentThread().getName() + " 執(zhí)行第" + (++count) + "次");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "高優(yōu)先級線程");
        highPriorityThread.setPriority(Thread.MAX_PRIORITY); // 優(yōu)先級10
        lowPriorityThread.start();
        highPriorityThread.start();
    }
}

運行結(jié)果 :高優(yōu)先級線程的執(zhí)行次數(shù)遠多于低優(yōu)先級線程,低優(yōu)先級線程長期 “饑餓”。

3. 饑餓的解決方案

核心思路: 保證資源分配的公平性 ,常用方案:

1.使用公平鎖: ReentrantLock 的公平鎖模式保證線程按 FIFO 順序獲取鎖,避免插隊;

private static final ReentrantLock LOCK = new ReentrantLock(true); // 公平鎖

2.避免線程優(yōu)先級差異:盡量將線程優(yōu)先級設置為相同(默認 5),減少調(diào)度器的偏好;

3.減少鎖持有時間:縮短同步代碼塊的執(zhí)行時間,讓鎖盡快釋放,增加低優(yōu)先級線程的獲取機會;

4.使用線程池:線程池的工作線程優(yōu)先級一致,且有任務隊列緩沖,避免個別線程長期搶占資源。

五、并發(fā)問題對比

問題類型

核心特征

線程狀態(tài)

排查難度

核心解決方案

死鎖

互相持有鎖,永久阻塞

BLOCKED

中(jstack 可直接檢測)

統(tǒng)一鎖順序、超時獲取、可中斷鎖

活鎖

無阻塞,但互相謙讓,無進展

RUNNABLE

高(無明顯報錯,需分析日志)

隨機重試延遲、優(yōu)先級機制、限制重試次數(shù)

饑餓

長期競爭不到資源,偶爾執(zhí)行

RUNNABLE

中(需統(tǒng)計執(zhí)行頻率)

公平鎖、統(tǒng)一優(yōu)先級、減少鎖持有時間

六、實戰(zhàn)避坑指南

1. 預防死鎖的最佳實踐

  • 最小化鎖范圍:僅在必要的代碼塊加鎖,縮短鎖持有時間;

  • 避免嵌套鎖:盡量不使用多層鎖嵌套,若必須使用,嚴格統(tǒng)一鎖獲取順序;

  • 使用定時鎖:優(yōu)先使用 tryLock(timeout) 替代無超時的鎖獲取;

  • 監(jiān)控鎖狀態(tài):通過 JMX/APM 工具監(jiān)控鎖的持有時間、競爭次數(shù),提前發(fā)現(xiàn)死鎖風險。

2. 通用優(yōu)化建議

  • 優(yōu)先使用并發(fā)工具: ConcurrentHashMap 、 CountDownLatch 等工具已封裝安全的并發(fā)邏輯,避免手動加鎖;

  • 避免手動線程管理:使用線程池( ThreadPoolExecutor )替代手動創(chuàng)建線程,統(tǒng)一管理線程生命周期;

  • 增加容錯機制:關(guān)鍵業(yè)務線程設置超時、重試、降級邏輯,避免因并發(fā)問題導致服務不可用;

  • 壓測驗證:上線前通過高并發(fā)壓測,模擬極端場景,提前暴露死鎖 / 活鎖 / 饑餓問題。

七、總結(jié)

本文深入剖析了 Java 并發(fā)編程中死鎖、活鎖、饑餓三類典型問題的產(chǎn)生原因、典型場景與解決方案。死鎖是最致命的問題,需通過破壞四大必要條件來預防;活鎖需打破同步重試的循環(huán);饑餓需保證資源分配的公平性。在實際開發(fā)中,應遵循 “預防大于排查” 的原則:通過統(tǒng)一鎖順序、使用公平鎖、縮短鎖持有時間等手段,從根源減少并發(fā)問題的發(fā)生;同時掌握 jstack、JConsole 等排查工具,快速定位已出現(xiàn)的問題。

到此這篇關(guān)于Java并發(fā)常見問題之死鎖/活鎖/饑餓的排查與解決方法的文章就介紹到這了,更多相關(guān)Java并發(fā)死鎖/活鎖/饑餓內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

邯郸市| 香格里拉县| 平遥县| 昌乐县| 图们市| 宜川县| 康平县| 即墨市| 工布江达县| 阳新县| 巴中市| 错那县| 延安市| 中山市| 浠水县| 临清市| 庆安县| 盘锦市| 兴海县| 德阳市| 金乡县| 澎湖县| 云南省| 商水县| 如东县| 沿河| 青铜峡市| 岫岩| 神农架林区| 焦作市| 屏南县| 中卫市| 茌平县| 阿克陶县| 新余市| 都兰县| 台州市| 乡宁县| 河源市| 屏东市| 枣强县|