自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java并發(fā)編程:線程活躍性問(wèn)題:死鎖、活鎖與饑餓

開(kāi)發(fā) 前端
活躍性問(wèn)題意味著程序永遠(yuǎn)無(wú)法得到運(yùn)行的最終結(jié)果。與之前提到的線程安全問(wèn)題導(dǎo)致的程序錯(cuò)誤相比,活躍性問(wèn)題的后果可能更嚴(yán)重。例如,若發(fā)生死鎖,程序會(huì)完全卡死無(wú)法運(yùn)行。

活躍性問(wèn)題意味著程序永遠(yuǎn)無(wú)法得到運(yùn)行的最終結(jié)果。與之前提到的線程安全問(wèn)題導(dǎo)致的程序錯(cuò)誤相比,活躍性問(wèn)題的后果可能更嚴(yán)重。例如,若發(fā)生死鎖,程序會(huì)完全卡死無(wú)法運(yùn)行。

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

1. 死鎖(Deadlock)

最常見(jiàn)的活躍性問(wèn)題是死鎖。當(dāng)兩個(gè)線程互相等待對(duì)方持有的資源,且都不釋放自己已持有的資源時(shí),就會(huì)導(dǎo)致永久阻塞。

代碼示例:

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 成功執(zhí)行");
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

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

輸出結(jié)果:

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

啟動(dòng)程序后會(huì)發(fā)現(xiàn),程序一直在運(yùn)行,但永遠(yuǎn)無(wú)法輸出線程 1 和線程 2 的執(zhí)行結(jié)果,說(shuō)明兩者都被卡住了。如果不強(qiáng)制終止進(jìn)程,它們將永遠(yuǎn)等待。

注意:后續(xù)章節(jié)會(huì)詳細(xì)講解synchronized關(guān)鍵字,目前只需知道它能確保同一時(shí)刻最多一個(gè)線程執(zhí)行代碼(需持有對(duì)應(yīng)鎖),以控制并發(fā)安全。

死鎖的必要條件

根據(jù)上述示例,可以分析死鎖發(fā)生的四個(gè)必要條件:

  • 互斥條件:資源一次只能被一個(gè)進(jìn)程或線程使用。例如,鎖被某個(gè)線程持有后,其他線程無(wú)法獲取,直到釋放。
  • 請(qǐng)求與保持條件:線程在持有第一個(gè)鎖的同時(shí)請(qǐng)求第二個(gè)鎖。例如,線程 1 持有鎖 A 后嘗試獲取鎖 B,且不釋放鎖 A。
  • 不可剝奪條件:鎖不會(huì)被外部強(qiáng)制剝奪。即沒(méi)有外界干預(yù)來(lái)終止死鎖。
  • 循環(huán)等待條件:多個(gè)線程形成環(huán)形等待鏈。例如,線程 A 等線程 B 釋放資源,線程 B 等線程 A 釋放資源;或多個(gè)線程形成 A→B→C→A 的循環(huán)等待鏈。

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

如何預(yù)防死鎖

如果線程一次只能獲取一個(gè)鎖,則不會(huì)發(fā)生死鎖。雖然不太實(shí)用,但這是最徹底的解決方案。

以下是兩種常用預(yù)防方法:

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

2. 活鎖(Livelock)

什么是活鎖

活鎖是第二種活躍性問(wèn)題。與死鎖類(lèi)似,程序無(wú)法得到最終結(jié)果,但線程并非完全阻塞,而是不斷嘗試執(zhí)行卻無(wú)法推進(jìn)。

例如:兩人迎面相遇,互相讓路,結(jié)果你往右我往左,再次相撞,最終誰(shuí)也無(wú)法通過(guò)。

代碼示例:

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);  // 模擬業(yè)務(wù)耗時(shí)

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

            System.out.println("執(zhí)行 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("無(wú)法獲取 lock1,釋放 lock2");
                lock2.unlock();
                continue;
            }

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

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

輸出結(jié)果:

獲取lock1,嘗試獲取lock2  
獲取lock2,嘗試獲取lock1  
無(wú)法獲取lock2,釋放lock1  
獲取lock2,嘗試獲取lock1  
無(wú)法獲取lock1,釋放lock2  
...(循環(huán))

從日志可見(jiàn),兩個(gè)線程不斷獲取和釋放鎖,但都無(wú)法完成操作。

注意:由于線程調(diào)度,此示例可能在運(yùn)行一段時(shí)間后自動(dòng)解除活鎖,但不影響理解其原理。

如何預(yù)防活鎖

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

修改代碼:

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

修改后運(yùn)行結(jié)果:

獲取lock1,嘗試獲取lock2  
獲取lock2,嘗試獲取lock1  
無(wú)法獲取lock1,釋放lock2  
獲取lock2  
執(zhí)行operation1  
獲取lock2,嘗試獲取lock1  
獲取lock1  
執(zhí)行operation2

此時(shí)活鎖問(wèn)題基本消失。

典型場(chǎng)景:消息隊(duì)列中某個(gè)錯(cuò)誤消息反復(fù)重試,導(dǎo)致線程忙但無(wú)結(jié)果。解決方法:

  1. 將錯(cuò)誤消息移至隊(duì)列尾部延遲處理;
  2. 限制重試次數(shù),超過(guò)后丟棄或特殊處理。

3. 饑餓(Starvation)

什么是饑餓

饑餓指線程長(zhǎng)期無(wú)法獲取資源(如 CPU 時(shí)間),導(dǎo)致無(wú)法運(yùn)行。常見(jiàn)場(chǎng)景:

  • 線程優(yōu)先級(jí)過(guò)低,長(zhǎng)期得不到調(diào)度;
  • 某線程持有鎖且不釋放(如無(wú)限循環(huán)),其他線程長(zhǎng)期等待。

饑餓的影響

導(dǎo)致程序響應(yīng)性差。例如,瀏覽器前端線程因后臺(tái)線程占用 CPU 無(wú)法響應(yīng)操作。

如何預(yù)防饑餓

  • 確保邏輯正確,及時(shí)釋放鎖;
  • 合理設(shè)置線程優(yōu)先級(jí)(或不設(shè)置優(yōu)先級(jí))。
責(zé)任編輯:武曉燕 來(lái)源: 程序猿技術(shù)充電站
相關(guān)推薦

2023-06-29 08:18:27

Java顯示鎖顯示條件隊(duì)列

2023-10-08 09:34:11

Java編程

2021-03-26 10:40:16

MySQL鎖等待死鎖

2024-09-27 09:31:25

2024-02-26 08:33:51

并發(fā)編程活躍性安全性

2011-12-29 13:31:15

Java

2025-02-19 00:05:18

Java并發(fā)編程

2025-02-17 00:00:25

Java并發(fā)編程

2023-07-05 08:18:54

Atomic類(lèi)樂(lè)觀鎖悲觀鎖

2009-06-17 11:23:00

Java多線程

2018-10-25 15:55:44

Java多線程鎖優(yōu)化

2019-04-12 15:14:44

Python線程

2020-07-06 08:03:32

Java悲觀鎖樂(lè)觀鎖

2023-08-25 09:36:43

Java編程

2022-07-10 20:49:57

javaVolatile線程

2010-05-24 14:04:48

JavaSwing多線程

2025-02-06 03:14:38

2025-01-10 07:10:00

2017-05-03 16:26:24

MySQL并發(fā)死鎖

2024-04-02 11:22:01

死鎖Java并發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)