成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java并發編程:線程活躍性問題:死鎖、活鎖與饑餓

開發 前端
活躍性問題意味著程序永遠無法得到運行的最終結果。與之前提到的線程安全問題導致的程序錯誤相比,活躍性問題的后果可能更嚴重。例如,若發生死鎖,程序會完全卡死無法運行。

活躍性問題意味著程序永遠無法得到運行的最終結果。與之前提到的線程安全問題導致的程序錯誤相比,活躍性問題的后果可能更嚴重。例如,若發生死鎖,程序會完全卡死無法運行。

最典型的三種活躍性問題是死鎖(Deadlock)、活鎖(Livelock)和饑餓(Starvation)。下面逐一介紹。

1. 死鎖(Deadlock)

最常見的活躍性問題是死鎖。當兩個線程互相等待對方持有的資源,且都不釋放自己已持有的資源時,就會導致永久阻塞。

代碼示例:

public class DeadLock {

    static Object lock1 = new Object();
    static Object lock2 = new Object();

    public static void main(String[] args) {

        new Thread(() -> {
            try {
                synchronized (lock1) {
                    Thread.sleep(500);
                    synchronized (lock2) {
                        System.out.println("Thread 1 成功執行");
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            try {
                synchronized (lock2) {
                    Thread.sleep(500);
                    synchronized (lock1) {
                        System.out.println("Thread 2 成功執行");
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

輸出結果:

Acquired lock1, trying to acquire lock2.  
Acquired lock2, trying to acquire lock1.

啟動程序后會發現,程序一直在運行,但永遠無法輸出線程 1 和線程 2 的執行結果,說明兩者都被卡住了。如果不強制終止進程,它們將永遠等待。

注意:后續章節會詳細講解synchronized關鍵字,目前只需知道它能確保同一時刻最多一個線程執行代碼(需持有對應鎖),以控制并發安全。

死鎖的必要條件

根據上述示例,可以分析死鎖發生的四個必要條件:

  • 互斥條件:資源一次只能被一個進程或線程使用。例如,鎖被某個線程持有后,其他線程無法獲取,直到釋放。
  • 請求與保持條件:線程在持有第一個鎖的同時請求第二個鎖。例如,線程 1 持有鎖 A 后嘗試獲取鎖 B,且不釋放鎖 A。
  • 不可剝奪條件:鎖不會被外部強制剝奪。即沒有外界干預來終止死鎖。
  • 循環等待條件:多個線程形成環形等待鏈。例如,線程 A 等線程 B 釋放資源,線程 B 等線程 A 釋放資源;或多個線程形成 A→B→C→A 的循環等待鏈。

??以上四個條件缺一不可!只要破壞任意一個條件,即可避免死鎖!

如何預防死鎖

如果線程一次只能獲取一個鎖,則不會發生死鎖。雖然不太實用,但這是最徹底的解決方案。

以下是兩種常用預防方法:

  • 按固定順序獲取鎖
    如果必須獲取多個鎖,設計時需要確保所有線程按相同順序獲取鎖。例如修改上述代碼:
// 線程 1 和線程 2 均按 lock1 → lock2 順序獲取
Thread1--> 獲取lock1--> 獲取lock2--> 執行成功;
Thread2--> 獲取lock1--> 獲取lock2--> 執行成功;
  • 超時放棄
    使用synchronized內置鎖時,線程會無限等待。而Lock接口的tryLock(long time, TimeUnit unit)方法允許設置等待時間。若超時未獲鎖,線程可主動釋放已持有的鎖,從而避免死鎖。

2. 活鎖(Livelock)

什么是活鎖

活鎖是第二種活躍性問題。與死鎖類似,程序無法得到最終結果,但線程并非完全阻塞,而是不斷嘗試執行卻無法推進。

例如:兩人迎面相遇,互相讓路,結果你往右我往左,再次相撞,最終誰也無法通過。

代碼示例:

public class Livelock {

    private Lock lock1 = new ReentrantLock(true);
    private Lock lock2 = new ReentrantLock(true);

    public static void main(String[] args) {
        Livelock livelock = new Livelock();
        new Thread(livelock::operation1, "T1").start();
        new Thread(livelock::operation2, "T2").start();
    }

    public void operation1() {
        while (true) {
            lock1.tryLock();
            System.out.println("獲取 lock1,嘗試獲取 lock2");
            sleep(50);  // 模擬業務耗時

            if (lock2.tryLock()) {
                System.out.println("獲取 lock2");
            } else {
                System.out.println("無法獲取 lock2,釋放 lock1");
                lock1.unlock();
                continue;
            }

            System.out.println("執行 operation1");
            break;
        }
        lock2.unlock();
        lock1.unlock();
    }

    public void operation2() {
        while (true) {
            lock2.tryLock();
            System.out.println("獲取 lock2,嘗試獲取 lock1");
            sleep(50);

            if (lock1.tryLock()) {
                System.out.println("獲取 lock1");
            } else {
                System.out.println("無法獲取 lock1,釋放 lock2");
                lock2.unlock();
                continue;
            }

            System.out.println("執行 operation2");
            break;
        }
        lock1.unlock();
        lock2.unlock();
    }

    private void sleep(long sleepTime) {
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

輸出結果:

獲取lock1,嘗試獲取lock2  
獲取lock2,嘗試獲取lock1  
無法獲取lock2,釋放lock1  
獲取lock2,嘗試獲取lock1  
無法獲取lock1,釋放lock2  
...(循環)

從日志可見,兩個線程不斷獲取和釋放鎖,但都無法完成操作。

注意:由于線程調度,此示例可能在運行一段時間后自動解除活鎖,但不影響理解其原理。

如何預防活鎖

活鎖的根源在于線程同時釋放鎖并重試。解決方法是為鎖獲取設置隨機等待時間,打破同步釋放的節奏:

修改代碼:

// 在 sleep 方法中增加隨機等待時間
private void sleep(long sleepTime) {
    try {
        Thread.sleep(sleepTime + (long)(Math.random() * 100));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

修改后運行結果:

獲取lock1,嘗試獲取lock2  
獲取lock2,嘗試獲取lock1  
無法獲取lock1,釋放lock2  
獲取lock2  
執行operation1  
獲取lock2,嘗試獲取lock1  
獲取lock1  
執行operation2

此時活鎖問題基本消失。

典型場景:消息隊列中某個錯誤消息反復重試,導致線程忙但無結果。解決方法:

  1. 將錯誤消息移至隊列尾部延遲處理;
  2. 限制重試次數,超過后丟棄或特殊處理。

3. 饑餓(Starvation)

什么是饑餓

饑餓指線程長期無法獲取資源(如 CPU 時間),導致無法運行。常見場景:

  • 線程優先級過低,長期得不到調度;
  • 某線程持有鎖且不釋放(如無限循環),其他線程長期等待。

饑餓的影響

導致程序響應性差。例如,瀏覽器前端線程因后臺線程占用 CPU 無法響應操作。

如何預防饑餓

  • 確保邏輯正確,及時釋放鎖;
  • 合理設置線程優先級(或不設置優先級)。
責任編輯:武曉燕 來源: 程序猿技術充電站
相關推薦

2023-06-29 08:18:27

Java顯示鎖顯示條件隊列

2021-03-26 10:40:16

MySQL鎖等待死鎖

2023-10-08 09:34:11

Java編程

2024-09-27 09:31:25

2024-02-26 08:33:51

并發編程活躍性安全性

2025-02-17 00:00:25

Java并發編程

2025-02-19 00:05:18

Java并發編程

2011-12-29 13:31:15

Java

2023-07-05 08:18:54

Atomic類樂觀鎖悲觀鎖

2009-06-17 11:23:00

Java多線程

2018-10-25 15:55:44

Java多線程鎖優化

2020-07-06 08:03:32

Java悲觀鎖樂觀鎖

2019-04-12 15:14:44

Python線程

2022-07-10 20:49:57

javaVolatile線程

2010-05-24 14:04:48

JavaSwing多線程

2017-05-03 16:26:24

MySQL并發死鎖

2023-08-25 09:36:43

Java編程

2025-01-10 07:10:00

2025-02-06 03:14:38

2024-02-27 17:46:25

并發程序CPU
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一区二区三区视频在线 | 成人亚洲精品 | 成人免费看片又大又黄 | 日韩视频三区 | 国产成人精品免费 | 日韩中文字幕 | 欧美三级三级三级爽爽爽 | www.97国产| 超碰在线播 | 欧美一级在线视频 | 国产小视频在线看 | 国产精品久久国产精品久久 | 亚洲欧美日韩精品 | 欧美激情一区 | 国内久久| 一级黄色片美国 | 欧美高清视频在线观看 | 欧美日韩在线精品 | 黄色免费三级 | 成人在线免费视频 | 天天综合91| 久久精品国产一区二区电影 | 国产日韩精品一区 | 欧美中文在线 | 国产伦精品一区二区三区四区视频 | 日韩不卡三区 | www国产成人免费观看视频,深夜成人网 | 亚洲成人久久久 | 色精品视频 | 91在线精品一区二区 | www.狠狠干| 色999日韩 | 久久久久国产精品午夜一区 | 国产免费xxx| 日韩视频一区二区三区 | 国产日韩久久 | 狠狠干网站 | 午夜影院免费体验区 | 亚洲国产精品久久 | 色橹橹欧美在线观看视频高清 | 欧美激情久久久 |