歡迎來到我們的多線程系列的第 3 部分!
- 在第 1 部分中,我們探討了 原子性 和 不變性。
- 在第 2 部分中,我們討論了飢餓。
在這一部分中,我們將深入研究多執行緒中死鎖的機制。原因是什麼,如何識別以及可以使用的預防策略,以避免將程式碼變成僵局。應用程式逐漸停止,通常沒有任何明顯的錯誤,讓開發人員感到困惑,系統凍結。
探索並發的複雜軌跡
理解死鎖的一個有用的類比是想像一個鐵路網絡,在相交的軌道上有多列火車。
由於每列火車都在等待下一列火車開動,因此沒有一列火車可以繼續行駛,從而導致僵局。在這種情況下,低效率的信號系統讓每趟列車在沒有先確認下一段是否空閒的情況下就進入了各自的路段,從而使所有列車陷入了一個無法打破的循環。
這個火車範例說明了多線程中的典型死鎖,其中線程(如火車)在等待其他資源被釋放時保留資源(軌道部分),但沒有一個可以前進。為了防止這種軟體死鎖,必須實施有效的資源管理策略(類似於更智慧的鐵路訊號),以避免循環依賴並確保每個執行緒的安全通道。
1.什麼是死鎖?
死鎖是執行緒(或行程)無限期阻塞、等待其他執行緒持有的資源的情況。這種情況會導致無法打破的依賴關係循環,任何涉及的執行緒都無法取得進展。在探索檢測、預防和解決方法之前,了解死鎖的基礎知識至關重要。
2. 死鎖發生的條件
要發生死鎖,必須同時滿足四個條件,稱為科夫曼條件:
互斥:至少一個資源必須以不可共享模式保存,這意味著一次只有一個執行緒可以使用它。
持有並等待:執行緒必須持有一種資源,並等待其他執行緒持有的其他資源。
無搶佔:無法從執行緒強行奪走資源。他們必須自願釋放。
循環等待:存在一個封閉的執行緒鏈,其中每個執行緒至少擁有鏈中下一個執行緒所需的一個資源。
我們用時序圖來理解
在上面的動畫中,
- 執行緒 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中文網其他相關文章!

JVM的工作原理是將Java代碼轉換為機器碼並管理資源。 1)類加載:加載.class文件到內存。 2)運行時數據區:管理內存區域。 3)執行引擎:解釋或編譯執行字節碼。 4)本地方法接口:通過JNI與操作系統交互。

JVM使Java實現跨平台運行。 1)JVM加載、驗證和執行字節碼。 2)JVM的工作包括類加載、字節碼驗證、解釋執行和內存管理。 3)JVM支持高級功能如動態類加載和反射。

Java應用可通過以下步驟在不同操作系統上運行:1)使用File或Paths類處理文件路徑;2)通過System.getenv()設置和獲取環境變量;3)利用Maven或Gradle管理依賴並測試。 Java的跨平台能力依賴於JVM的抽象層,但仍需手動處理某些操作系統特定的功能。

Java在不同平台上需要進行特定配置和調優。 1)調整JVM參數,如-Xms和-Xmx設置堆大小。 2)選擇合適的垃圾回收策略,如ParallelGC或G1GC。 3)配置Native庫以適應不同平台,這些措施能讓Java應用在各種環境中發揮最佳性能。

Osgi,Apachecommonslang,JNA和JvMoptionsareeForhandlingForhandlingPlatform-specificchallengesinjava.1)osgimanagesdeppedendendencenciesandisolatescomponents.2)apachecommonslangprovidesitorityfunctions.3)

JVMmanagesgarbagecollectionacrossplatformseffectivelybyusingagenerationalapproachandadaptingtoOSandhardwaredifferences.ItemploysvariouscollectorslikeSerial,Parallel,CMS,andG1,eachsuitedfordifferentscenarios.Performancecanbetunedwithflagslike-XX:NewRa

Java代碼可以在不同操作系統上無需修改即可運行,這是因為Java的“一次編寫,到處運行”哲學,由Java虛擬機(JVM)實現。 JVM作為編譯後的Java字節碼與操作系統之間的中介,將字節碼翻譯成特定機器指令,確保程序在任何安裝了JVM的平台上都能獨立運行。

Java程序的編譯和執行通過字節碼和JVM實現平台獨立性。 1)編寫Java源碼並編譯成字節碼。 2)使用JVM在任何平台上執行字節碼,確保代碼的跨平台運行。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3漢化版
中文版,非常好用

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。