java死鎖檢測和恢復(java自身能防止死鎖,但不能對死鎖進行檢測)

本文目錄一覽:

java死鎖怎麼解決

可以試試synchronized,可用來給對象和方法或者代碼塊加鎖,當它鎖定一個方法或者一個代碼塊的時候,同一時刻最多只有一個線程執行這段代碼。

在java中,死鎖形成的原因是?

死鎖是進程死鎖的簡稱,是由Dijkstra於1965年研究銀行家演算法時首先提出來的。它是計算機操作系統乃至並發程序設計中最難處理的問題之一。實際上,死鎖問題不僅在計算機系統中存在,在我們日常生活中它也廣泛存在。

1.什麼是死鎖

我們先看看這樣一個生活中的例子:在一條河上有一座橋,橋面較窄,只能容納一輛汽車通過,無法讓兩輛汽車並行。如果有兩輛汽車A和B分別由橋的兩端駛上該橋,則對於A車來說,它走過橋面左面的一段路(即佔有了橋的一部分資源),要想過橋還須等待B車讓出右邊的橋面,此時A車不能前進;對於B車來說,它走過橋面右邊的一段路(即佔有了橋的一部分資源),要想過橋還須等待A車讓出左邊的橋面,此時B車也不能前進。兩邊的車都不倒車,結果造成互相等待對方讓出橋面,但是誰也不讓路,就會無休止地等下去。這種現象就是死鎖。如果把汽車比做進程,橋面作為資源,那麽上述問題就描述為:進程A佔有資源R1,等待進程B佔有的資源Rr;進程B佔有資源Rr,等待進程A佔有的資源R1。而且資源R1和Rr只允許一個進程佔用,即:不允許兩個進程同時佔用。結果,兩個進程都不能繼續執行,若不採取其它措施,這種循環等待狀況會無限期持續下去,就發生了進程死鎖。

在計算機系統中,涉及軟體,硬體資源都可能發生死鎖。例如:系統中只有一台CD-ROM驅動器和一台印表機,某一個進程佔有了CD-ROM驅動器,又申請印表機;另一進程佔有了印表機,還申請CD-ROM。結果,兩個進程都被阻塞,永遠也不能自行解除。

所謂死鎖,是指多個進程循環等待它方佔有的資源而無限期地僵持下去的局面。很顯然,如果沒有外力的作用,那麽死鎖涉及到的各個進程都將永遠處於封鎖狀態。從上面的例子可以看出,計算機系統產生死鎖的根本原因就是資源有限且操作不當。即:一種原因是系統提供的資源太少了,遠不能滿足並發進程對資源的需求。這種競爭資源引起的死鎖是我們要討論的核心。例如:消息是一種臨時性資源。某一時刻,進程A等待進程B發來的消息,進程B等待進程C發來的消息,而進程C又等待進程A發來的消息。消息未到,A,B,C三個進程均無法向前推進,也會發生進程通信上的死鎖。另一種原因是由於進程推進順序不合適引發的死鎖。資源少也未必一定產生死鎖。就如同兩個人過獨木橋,如果兩個人都要先過,在獨木橋上僵持不肯後退,必然會應競爭資源產生死鎖;但是,如果兩個人上橋前先看一看有無對方的人在橋上,當無對方的人在橋上時自己才上橋,那麽問題就解決了。所以,如果程序設計得不合理,造成進程推進的順序不當,也會出現死鎖。

2.產生死鎖的必要條件

從以上分析可見,如果在計算機系統中同時具備下面四個必要條件時,那麽會發生死鎖。換句話說,只要下面四個條件有一個不具備,系統就不會出現死鎖。

〈1〉互斥條件。即某個資源在一段時間內只能由一個進程佔有,不能同時被兩個或兩個以上的進程佔有。這種獨佔資源如CD-ROM驅動器,印表機等等,必須在佔有該資源的進程主動釋放它之後,其它進程才能佔有該資源。這是由資源本身的屬性所決定的。如獨木橋就是一種獨佔資源,兩方的人不能同時過橋。

〈2〉不可搶佔條件。進程所獲得的資源在未使用完畢之前,資源申請者不能強行地從資源佔有者手中奪取資源,而只能由該資源的佔有者進程自行釋放。如過獨木橋的人不能強迫對方後退,也不能非法地將對方推下橋,必須是橋上的人自己過橋後空出橋面(即主動釋放佔有資源),對方的人才能過橋。

〈3〉佔有且申請條件。進程至少已經佔有一個資源,但又申請新的資源;由於該資源已被另外進程佔有,此時該進程阻塞;但是,它在等待新資源之時,仍繼續佔用已佔有的資源。還以過獨木橋為例,甲乙兩人在橋上相遇。甲走過一段橋面(即佔有了一些資源),還需要走其餘的橋面(申請新的資源),但那部分橋面被乙佔有(乙走過一段橋面)。甲過不去,前進不能,又不後退;乙也處於同樣的狀況。

〈4〉循環等待條件。存在一個進程等待序列{P1,P2,…,Pn},其中P1等待P2所佔有的某一資源,P2等待P3所佔有的某一源,……,而Pn等待P1所佔有的的某一資源,形成一個進程循環等待環。就像前面的過獨木橋問題,甲等待乙佔有的橋面,而乙又等待甲佔有的橋面,從而彼此循環等待。

上面我們提到的這四個條件在死鎖時會同時發生。也就是說,只要有一個必要條件不滿足,則死鎖就可以排除。

8.2 死鎖的預防

前面介紹了死鎖發生時的四個必要條件,只要破壞這四個必要條件中的任意一個條件,死鎖就不會發生。這就為我們解決死鎖問題提供了可能。一般地,解決死鎖的方法分為死鎖的預防,避免,檢測與恢復三種(注意:死鎖的檢測與恢復是一個方法)。我們將在下面分別加以介紹。

死鎖的預防是保證系統不進入死鎖狀態的一種策略。它的基本思想是要求進程申請資源時遵循某種協議,從而打破產生死鎖的四個必要條件中的一個或幾個,保證系統不會進入死鎖狀態。

java 死鎖

死鎖

死鎖是這樣一種情形:多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由於線程被無限期地阻塞,因此程序不可能正常終止。

導致死鎖的根源在於不適當地運用「synchronized」關鍵詞來管理線程對特定對象的訪問。「synchronized」關鍵詞的作用是,確保在某個時刻只有一個線程被允許執行特定的代碼塊,因此,被允許執行的線程首先必須擁有對變數或對象的排他性的訪問權。當線程訪問對象時,線程會給對象加鎖,而這個鎖導致其它也想訪問同一對象的線程被阻塞,直至第一個線程釋放它加在對象上的鎖。

由於這個原因,在使用「synchronized」關鍵詞時,很容易出現兩個線程互相等待對方做出某個動作的情形。代碼一是一個導致死鎖的簡單例子。

//代碼一

class Deadlocker {

int field_1;

private Object lock_1 = new int[1];

int field_2;

private Object lock_2 = new int[1];

public void method1(int value) {

「synchronized」 (lock_1) {

「synchronized」 (lock_2) {

field_1 = 0; field_2 = 0;

}

}

}

public void method2(int value) {

「synchronized」 (lock_2) {

「synchronized」 (lock_1) {

field_1 = 0; field_2 = 0;

}

}

}

}

參考代碼一,考慮下面的過程:

◆ 一個線程(ThreadA)調用method1()。

◆ ThreadA在lock_1上同步,但允許被搶先執行。

◆ 另一個線程(ThreadB)開始執行。

◆ ThreadB調用method2()。

◆ ThreadB獲得lock_2,繼續執行,企圖獲得lock_1。但ThreadB不能獲得lock_1,因為ThreadA佔有lock_1。

◆ 現在,ThreadB阻塞,因為它在等待ThreadA釋放lock_1。

◆ 現在輪到ThreadA繼續執行。ThreadA試圖獲得lock_2,但不能成功,因為lock_2已經被ThreadB佔有了。

◆ ThreadA和ThreadB都被阻塞,程序死鎖。

當然,大多數的死鎖不會這麼顯而易見,需要仔細分析代碼才能看出,對於規模較大的多線程程序來說尤其如此。好的線程分析工具,例如JProbe Threadalyzer能夠分析死鎖並指出產生問題的代碼位置。

隱性死鎖

隱性死鎖由於不規範的編程方式引起,但不一定每次測試運行時都會出現程序死鎖的情形。由於這個原因,一些隱性死鎖可能要到應用正式發布之後才會被發現,因此它的危害性比普通死鎖更大。下面介紹兩種導致隱性死鎖的情況:加鎖次序和佔有並等待。

加鎖次序

當多個並發的線程分別試圖同時佔有兩個鎖時,會出現加鎖次序衝突的情形。如果一個線程佔有了另一個線程必需的鎖,就有可能出現死鎖。考慮下面的情形,ThreadA和ThreadB兩個線程分別需要同時擁有lock_1、lock_2兩個鎖,加鎖過程可能如下:

◆ ThreadA獲得lock_1;

◆ ThreadA被搶佔,VM調度程序轉到ThreadB;

◆ ThreadB獲得lock_2;

◆ ThreadB被搶佔,VM調度程序轉到ThreadA;

◆ ThreadA試圖獲得lock_2,但lock_2被ThreadB佔有,所以ThreadA阻塞;

◆ 調度程序轉到ThreadB;

◆ ThreadB試圖獲得lock_1,但lock_1被ThreadA佔有,所以ThreadB阻塞;

◆ ThreadA和ThreadB死鎖。

必須指出的是,在代碼絲毫不做變動的情況下,有些時候上述死鎖過程不會出現,VM調度程序可能讓其中一個線程同時獲得lock_1和lock_2兩個鎖,即線程獲取兩個鎖的過程沒有被中斷。在這種情形下,常規的死鎖檢測很難確定錯誤所在。

佔有並等待

如果一個線程獲得了一個鎖之後還要等待來自另一個線程的通知,可能出現另一種隱性死鎖,考慮代碼二。

//代碼二

public class queue {

static java.lang.Object queueLock_;

Producer producer_;

Consumer consumer_;

public class Producer {

void produce() {

while (!done) {

「synchronized」 (queueLock_) {

produceItemAndAddItToQueue();

「synchronized」 (consumer_) {

consumer_.notify();

}

}

}

}

public class Consumer {

consume() {

while (!done) {

「synchronized」 (queueLock_) {

「synchronized」 (consumer_) {

consumer_.wait();

}

removeItemFromQueueAndProcessIt();

}

}

}

}

}

}

在代碼二中,Producer向隊列加入一項新的內容後通知Consumer,以便它處理新的內容。問題在於,Consumer可能保持加在隊列上的鎖,阻止Producer訪問隊列,甚至在Consumer等待Producer的通知時也會繼續保持鎖。這樣,由於Producer不能向隊列添加新的內容,而Consumer卻在等待Producer加入新內容的通知,結果就導致了死鎖。

在等待時佔有的鎖是一種隱性的死鎖,這是因為事情可能按照比較理想的情況發展—Producer線程不需要被Consumer佔據的鎖。儘管如此,除非有絕對可靠的理由肯定Producer線程永遠不需要該鎖,否則這種編程方式仍是不安全的。有時「佔有並等待」還可能引發一連串的線程等待,例如,線程A佔有線程B需要的鎖並等待,而線程B又佔有線程C需要的鎖並等待等。

要改正代碼二的錯誤,只需修改Consumer類,把wait()移出「synchronized」()即可。

怎麼處理JAVA多線程死鎖問題?

有兩種實現方法,分別是繼承Thread類與實現Runnable

介面

用synchronized關鍵字修飾同步方法

反對使用stop(),是因為它不安全。它會解除由線程獲取的所有鎖定,而且如果對象處於一種不連貫狀態,那麼

其他線程能在那種狀態下檢查和修改它們。結果很難檢查出真正的問題所在。suspend()方法容易發生死鎖。調用

suspend()的時候,目標線程會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何線程都不能訪問鎖定

的資源,除非被”掛起”的線程恢復運行。對任何線程來說,如果它們想恢複目標線程,同時又試圖使用任何一個

鎖定的資源,就會造成死鎖。所以不應該使用suspend(),而應在自己的Thread類中置入一個標誌,指出線程應該

活動還是掛起。若標誌指出線程應該掛起,便用wait()命其進入等待狀態。若標誌指出線程應當恢復,則用一個

notify()重新啟動線程。

java程序死鎖問題,怎麼解決

在 IBM Bluemix 雲平台上開發並部署您的下一個應用。

開始您的試用

Java 語言通過 synchronized 關鍵字來保證原子性,這是因為每一個 Object 都有一個隱含的鎖,這個也稱作監視器對象。在進入 synchronized 之前自動獲取此內部鎖,而一旦離開此方式,無論是完成或者中斷都會自動釋放鎖。顯然這是一個獨佔鎖,每個鎖請求之間是互斥的。相對於眾多高級鎖 (Lock/ReadWriteLock 等),synchronized 的代價都比後者要高。但是 synchronzied 的語法比較簡單,而且也比較容易使用和理解。Lock 一旦調用了 lock() 方法獲取到鎖而未正確釋放的話很有可能造成死鎖,所以 Lock 的釋放操作總是跟在 finally 代碼塊裡面,這在代碼結構上也是一次調整和冗餘。Lock 的實現已經將硬體資源用到了極致,所以未來可優化的空間不大,除非硬體有了更高的性能,但是 synchronized 只是規範的一種實現,這在不同的平台不同的硬體還有很高的提升空間,未來 Java 鎖上的優化也會主要在這上面。既然 synchronzied 都不可能避免死鎖產生,那麼死鎖情況會是經常容易出現的錯誤,下面具體描述死鎖發生的原因及解決方法。

死鎖描述

死鎖是操作系統層面的一個錯誤,是進程死鎖的簡稱,最早在 1965 年由 Dijkstra 在研究銀行家演算法時提出的,它是計算機操作系統乃至整個並發程序設計領域最難處理的問題之一。

事實上,計算機世界有很多事情需要多線程方式去解決,因為這樣才能最大程度上利用資源,才能體現出計算的高效。但是,實際上來說,計算機系統中有很多一次只能由一個進程使用的資源的情況,例如印表機,同時只能有一個進程式控制制它。在多通道程序設計環境中,若干進程往往要共享這類資源,而且一個進程所需要的資源還很有可能不止一個。因此,就會出現若干進程競爭有限資源,又推進順序不當,從而構成無限期循環等待的局面。我們稱這種狀態為死鎖。簡單一點描述,死鎖是指多個進程循環等待它方佔有的資源而無限期地僵持下去的局面。很顯然,如果沒有外力的作用,那麼死鎖涉及到的各個進程都將永遠處於封鎖狀態。

系統發生死鎖現象不僅浪費大量的系統資源,甚至導致整個系統崩潰,帶來災難性後果。所以,對於死鎖問題在理論上和技術上都必須予以高度重視。

銀行家演算法

一個銀行家如何將一定數目的資金安全地借給若干個客戶,使這些客戶既能借到錢完成要乾的事,同時銀行家又能收回全部資金而不至於破產。銀行家就像一個操作系統,客戶就像運行的進程,銀行家的資金就是系統的資源。

銀行家演算法需要確保以下四點:

當一個顧客對資金的最大需求量不超過銀行家現有的資金時就可接納該顧客;

顧客可以分期貸款, 但貸款的總數不能超過最大需求量;

當銀行家現有的資金不能滿足顧客尚需的貸款數額時,對顧客的貸款可推遲支付,但總能使顧客在有限的時間裡得到貸款;

當顧客得到所需的全部資金後,一定能在有限的時間裡歸還所有的資金。

解決死鎖的4種基本方法(值得收藏)

解決死鎖的4種基本方法(文末有驚喜)

1、預防死鎖:通過設置一些限制條件,去破壞產生死鎖的必要條件

2、避免死鎖:在資源分配過程中,使用某種方法避免系統進入不安全的狀態,從而避免發生死鎖

3、檢測死鎖:允許死鎖的發生,但是通過系統的檢測之後,採取一些措施,將死鎖清除掉

4、解除死鎖:該方法與檢測死鎖配合使用

死鎖介紹

死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。

產生條件

雖然進程在運行過程中,可能發生死鎖,但死鎖的發生也必須具備一定的條件,死鎖的發生必須具備以下四個必要條件。

1)互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程佔用。如果此時還有其它進程請求資源,則請求者只能等待,直至佔有資源的進程用畢釋放。

2)請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程佔有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。

3)不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。

4)環路等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源。

小關注來一波,我為你們準備了最新java學習資料文檔以及高清視頻教程,有需要的小夥伴掃一掃更直接

原創文章,作者:SETFR,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/313606.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
SETFR的頭像SETFR
上一篇 2025-01-07 09:43
下一篇 2025-01-07 09:43

相關推薦

  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • Java Bean載入過程

    Java Bean載入過程涉及到類載入器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean載入的過程。 一、類載入器 類載入器是Java虛擬機…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發布。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29
  • Java判斷字元串是否存在多個

    本文將從以下幾個方面詳細闡述如何使用Java判斷一個字元串中是否存在多個指定字元: 一、字元串遍歷 字元串是Java編程中非常重要的一種數據類型。要判斷字元串中是否存在多個指定字元…

    編程 2025-04-29
  • VSCode為什麼無法運行Java

    解答:VSCode無法運行Java是因為默認情況下,VSCode並沒有集成Java運行環境,需要手動添加Java運行環境或安裝相關插件才能實現Java代碼的編寫、調試和運行。 一、…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29
  • Java 8 Group By 會影響排序嗎?

    是的,Java 8中的Group By會對排序產生影響。本文將從多個方面探討Group By對排序的影響。 一、Group By的概述 Group By是SQL中的一種常見操作,它…

    編程 2025-04-29

發表回復

登錄後才能評論