首頁 Java java教程 多執行緒概念 部分死鎖

多執行緒概念 部分死鎖

Nov 05, 2024 am 10:03 AM

歡迎來到我們的多線程系列的第 3 部分!

  • 在第 1 部分中,我們探討了 原子性不變性
  • 在第 2 部分中,我們討論了飢餓

在這一部分中,我們將深入研究多執行緒中死鎖的機制。原因是什麼,如何識別以及可以使用的預防策略,以避免將程式碼變成僵局。應用程式逐漸停止,通常沒有任何明顯的錯誤,讓開發人員感到困惑,系統凍結。

Multithreading Concepts Part  Deadlock

探索並發的複雜軌跡

理解死鎖的一個有用的類比是想像一個鐵路網絡,在相交的軌道上有多列火車。

由於每列火車都在等待下一列火車開動,因此沒有一列火車可以繼續行駛,從而導致僵局。在這種情況下,低效率的信號系統讓每趟列車在沒有先確認下一段是否空閒的情況下就進入了各自的路段,從而使所有列車陷入了一個無法打破的循環。

這個火車範例說明了多線程中的典型死鎖,其中線程(如火車)在等待其他資源被釋放時保留資源(軌道部分),但沒有一個可以前進。為了防止這種軟體死鎖,必須實施有效的資源管理策略(類似於更智慧的鐵路訊號),以避免循環依賴並確保每個執行緒的安全通道。

1.什麼是死鎖?

死鎖是執行緒(或行程)無限期阻塞、等待其他執行緒持有的資源的情況。這種情況會導致無法打破的依賴關係循環,任何涉及的執行緒都無法取得進展。在探索檢測、預防和解決方法之前,了解死鎖的基礎知識至關重要。

2. 死鎖發生的條件

要發生死鎖,必須同時滿足四個條件,稱為科夫曼條件:

  • 互斥:至少一個資源必須以不可共享模式保存,這意味著一次只有一個執行緒可以使用它。

  • 持有並等待:執行緒必須持有一種資源,並等待其他執行緒持有的其他資源。

  • 無搶佔:無法從執行緒強行奪走資源。他們必須自願釋放。

  • 循環等待:存在一個封閉的執行緒鏈,其中每個執行緒至少擁有鏈中下一個執行緒所需的一個資源。

Multithreading Concepts Part  Deadlock

我們用時序圖來理解

Multithreading Concepts Part  Deadlock

在上面的動畫中,

  • 執行緒 A 持有資源 1 並等待資源 2
  • 當執行緒 B 持有資源 2 並等待資源 1

上面共享的所有四個死鎖條件都存在,這會導致無限期的阻塞。打破其中任何一個都可以防止僵局。

3. 偵測/監控死鎖

偵測死鎖,尤其是在大規模應用程式中,可能具有挑戰性。然而,以下方法可以幫助識別死鎖

  • 工具: Java 的 JConsole、VisualVM 以及 IDE 中的執行緒分析器可以即時偵測死鎖。
  • 執行緒轉儲與日誌:分析執行緒轉儲可以揭示等待執行緒及其所持有的資源。

有關如何調試/監視死鎖的詳細概述,請訪問使用 VisualVM 和 jstack 調試和監視死鎖

4. 預防死鎖的策略

  • 應用等待死亡和傷口等待方案
    等待死亡方案:當一個執行緒請求另一個執行緒持有的鎖時,資料庫會評估相對優先權(通常基於每個執行緒的時間戳記)。如果請求執行緒的優先權較高,則等待;否則,它會死掉(重新啟動)。
    Wound-Wait 方案:如果請求執行緒具有較高優先權,它會透過強制釋放鎖來破壞(搶佔)較低優先權執行緒。

  • 共享狀態的不可變物件
    盡可能將共享狀態設計為不可變。由於不可變物件無法修改,因此它們無需鎖定即可進行並發訪問,從而降低死鎖風險並簡化程式碼。

  • 使用具有逾時的 tryLock 來取得鎖定:與標準同步區塊不同,ReentrantLock 允許使用 tryLock(timeout, unit) 在指定時間內嘗試取得鎖定。如果在此時間內未取得鎖,它將釋放資源,防止無限期阻塞。

ReentrantLock lock1 = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();

public void acquireLocks() {
    try {
        if (lock1.tryLock(100, TimeUnit.MILLISECONDS)) {
            try {
                if (lock2.tryLock(100, TimeUnit.MILLISECONDS)) {
                    // Critical section
                }
            } finally {
                lock2.unlock();
            }
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } finally {
        lock1.unlock();
    }
}

登入後複製
  • 鎖定排序與釋放 為鎖獲取設定嚴格的全域順序。如果所有執行緒以一致的順序取得鎖,則不太可能形成循環依賴,從而避免死鎖。例如,在整個程式碼庫中始終先取得 lock1,然後再取得 lock2。這種做法在較大的應用程式中可能具有挑戰性,但對於降低死鎖風險非常有效。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockOrderingExample {

    private static final Lock lock1 = new ReentrantLock();
    private static final Lock lock2 = new ReentrantLock();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            acquireLocksInOrder(lock1, lock2);
        });

        Thread thread2 = new Thread(() -> {
            acquireLocksInOrder(lock1, lock2);
        });

        thread1.start();
        thread2.start();
    }

    private static void acquireLocksInOrder(Lock firstLock, Lock secondLock) {
        try {
            firstLock.lock();
            System.out.println(Thread.currentThread().getName() + " acquired lock1");

            secondLock.lock();
            System.out.println(Thread.currentThread().getName() + " acquired lock2");

            // Perform some operations

        } finally {
            secondLock.unlock();
            System.out.println(Thread.currentThread().getName() + " released lock2");

            firstLock.unlock();
            System.out.println(Thread.currentThread().getName() + " released lock1");
        }
    }
}
登入後複製
  • 使用線程安全/並發集合:Java 的java.util.concurrent 套件提供了常見資料結構(ConcurrentHashMap、CopyOnWriteArrayList 等)的線程安全實現,可以在內部處理同步,減少需要顯式鎖。這些集合最大限度地減少了死鎖,因為它們旨在使用內部分區等技術來避免明確鎖定的需要。

  • 避免巢狀鎖
    盡量減少在同一塊內取得多個鎖以避免循環依賴。如果需要嵌套鎖,請使用一致的鎖定順序

軟體工程師的要點

  • 每當您建立需要鎖定的設計時,就有可能出現死鎖。
  • 死鎖是由進程之間的依賴關係循環引起的阻塞問題。沒有任何行程可以取得進展,因為每個行程都在等待另一個行程持有的資源,而且沒有一個行程可以繼續釋放資源。
  • 死鎖更為嚴重,因為它會完全停止所涉及的進程,並且需要打破死鎖循環才能恢復。
  • 死鎖只有當有兩個不同的鎖時才會發生,即當您持有一個鎖並等待另一個鎖釋放時。 (但是,死鎖還有更多條件)。
  • 線程安全並不意味著無死鎖。它僅保證程式碼將根據其介面進行操作,即使是從多個執行緒呼叫時也是如此。使類別線程安全通常包括添加鎖以確保安全執行。

尾奏

無論您是初學者還是經驗豐富的開發人員,了解死鎖對於在並發系統中編寫健全、高效的程式碼至關重要。在本文中,我們探討了死鎖是什麼、原因以及預防死鎖的實用方法。透過實施有效的資源分配策略、分析任務依賴性以及利用線程轉儲和死鎖檢測工具等工具,開發人員可以最大限度地降低死鎖風險並優化程式碼以實現平滑的並發。

當我們繼續了解多執行緒的核心概念時,請繼續關注本系列的下一篇文章。我們將深入關鍵部分,了解如何在多個執行緒之間安全地管理共享資源。我們也將討論競爭條件的概念,這是一種常見的並發問題,如果不加以控制,可能會導致不可預測的行為和錯誤。

透過每一步,您都會更深入地了解如何使您的應用程式執行緒安全、高效且具有彈性。不斷突破多執行緒知識的界限,建立更好、效能更高的軟體!

參考

  • Stackoverflow
  • 資訊圖表
  • 如何偵測並修復死鎖

以上是多執行緒概念 部分死鎖的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

公司安全軟件導致應用無法運行?如何排查和解決? 公司安全軟件導致應用無法運行?如何排查和解決? Apr 19, 2025 pm 04:51 PM

公司安全軟件導致部分應用無法正常運行的排查與解決方法許多公司為了保障內部網絡安全,會部署安全軟件。 ...

如何使用MapStruct簡化系統對接中的字段映射問題? 如何使用MapStruct簡化系統對接中的字段映射問題? Apr 19, 2025 pm 06:21 PM

系統對接中的字段映射處理在進行系統對接時,常常會遇到一個棘手的問題:如何將A系統的接口字段有效地映�...

如何優雅地獲取實體類變量名構建數據庫查詢條件? 如何優雅地獲取實體類變量名構建數據庫查詢條件? Apr 19, 2025 pm 11:42 PM

在使用MyBatis-Plus或其他ORM框架進行數據庫操作時,經常需要根據實體類的屬性名構造查詢條件。如果每次都手動...

如何將姓名轉換為數字以實現排序並保持群組中的一致性? 如何將姓名轉換為數字以實現排序並保持群組中的一致性? Apr 19, 2025 pm 11:30 PM

將姓名轉換為數字以實現排序的解決方案在許多應用場景中,用戶可能需要在群組中進行排序,尤其是在一個用...

IntelliJ IDEA是如何在不輸出日誌的情況下識別Spring Boot項目的端口號的? IntelliJ IDEA是如何在不輸出日誌的情況下識別Spring Boot項目的端口號的? Apr 19, 2025 pm 11:45 PM

在使用IntelliJIDEAUltimate版本啟動Spring...

Java對像如何安全地轉換為數組? Java對像如何安全地轉換為數組? Apr 19, 2025 pm 11:33 PM

Java對象與數組的轉換:深入探討強制類型轉換的風險與正確方法很多Java初學者會遇到將一個對象轉換成數組的�...

電商平台SKU和SPU數據庫設計:如何兼顧用戶自定義屬性和無屬性商品? 電商平台SKU和SPU數據庫設計:如何兼顧用戶自定義屬性和無屬性商品? Apr 19, 2025 pm 11:27 PM

電商平台SKU和SPU表設計詳解本文將探討電商平台中SKU和SPU的數據庫設計問題,特別是如何處理用戶自定義銷售屬...

使用TKMyBatis進行數據庫查詢時,如何優雅地獲取實體類變量名構建查詢條件? 使用TKMyBatis進行數據庫查詢時,如何優雅地獲取實體類變量名構建查詢條件? Apr 19, 2025 pm 09:51 PM

在使用TKMyBatis進行數據庫查詢時,如何優雅地獲取實體類變量名以構建查詢條件,是一個常見的難題。本文將針...

See all articles