摘要:死鎖通常發(fā)生在多個線程同時但以不同的順序請求同一組鎖的時候?,F(xiàn)在和對象被兩個不同的線程鎖住了。更復雜的死鎖死鎖可能不止包含個線程,這讓檢測死鎖變得更加困難。
死鎖是兩個或更多線程阻塞著等待其它處于死鎖狀態(tài)的線程所持有的鎖。死鎖通常發(fā)生在多個線程同時但以不同的順序請求同一組鎖的時候。
例如,如果線程1鎖住了A,然后嘗試對B進行加鎖,同時線程2已經鎖住了B,接著嘗試對A進行加鎖,這時死鎖就發(fā)生了。線程1永遠得不到B,線程2也永遠得不到A,并且它們永遠也不會知道發(fā)生了這樣的事情。為了得到彼此的對象(A和B),它們將永遠阻塞下去。這種情況就是一個死鎖。
該情況如下:
Thread 1 locks A, waits for B Thread 2 locks B, waits for A
這里有一個TreeNode類的例子,它調用了不同實例的synchronized方法:
public class TreeNode { TreeNode parent = null; List children = new ArrayList(); public synchronized void addChild(TreeNode child){ if(!this.children.contains(child)) { this.children.add(child); child.setParentOnly(this); } } public synchronized void addChildOnly(TreeNode child){ if(!this.children.contains(child){ this.children.add(child); } } public synchronized void setParent(TreeNode parent){ this.parent = parent; parent.addChildOnly(this); } public synchronized void setParentOnly(TreeNode parent){ this.parent = parent; } }
如果線程1調用parent.addChild(child)方法的同時有另外一個線程2調用child.setParent(parent)方法,兩個線程中的parent表示的是同一個對象,child亦然,此時就會發(fā)生死鎖。下面的偽代碼說明了這個過程:
Thread 1: parent.addChild(child); //locks parent --> child.setParentOnly(parent); Thread 2: child.setParent(parent); //locks child --> parent.addChildOnly()
首先線程1調用parent.addChild(child)。因為addChild()是同步的,所以線程1會對parent對象加鎖以不讓其它線程訪問該對象。
然后線程2調用child.setParent(parent)。因為setParent()是同步的,所以線程2會對child對象加鎖以不讓其它線程訪問該對象。
現(xiàn)在child和parent對象被兩個不同的線程鎖住了。接下來線程1嘗試調用child.setParentOnly()方法,但是由于child對象現(xiàn)在被線程2鎖住的,所以該調用會被阻塞。線程2也嘗試調用parent.addChildOnly(),但是由于parent對象現(xiàn)在被線程1鎖住,導致線程2也阻塞在該方法處?,F(xiàn)在兩個線程都被阻塞并等待著獲取另外一個線程所持有的鎖。
注意:像上文描述的,這兩個線程需要同時調用parent.addChild(child)和child.setParent(parent)方法,并且是同一個parent對象和同一個child對象,才有可能發(fā)生死鎖。上面的代碼可能運行一段時間才會出現(xiàn)死鎖。
這些線程需要同時獲得鎖。舉個例子,如果線程1稍微領先線程2,然后成功地鎖住了A和B兩個對象,那么線程2就會在嘗試對B加鎖的時候被阻塞,這樣死鎖就不會發(fā)生。因為線程調度通常是不可預測的,因此沒有一個辦法可以準確預測什么時候死鎖會發(fā)生,僅僅是可能會發(fā)生。
更復雜的死鎖死鎖可能不止包含2個線程,這讓檢測死鎖變得更加困難。下面是4個線程發(fā)生死鎖的例子:
Thread 1 locks A, waits for B Thread 2 locks B, waits for C Thread 3 locks C, waits for D Thread 4 locks D, waits for A
線程1等待線程2,線程2等待線程3,線程3等待線程4,線程4等待線程1。
數(shù)據(jù)庫的死鎖更加復雜的死鎖場景發(fā)生在數(shù)據(jù)庫事務中。一個數(shù)據(jù)庫事務可能由多條SQL更新請求組成。當在一個事務中更新一條記錄,這條記錄就會被鎖住避免其他事務的更新請求,直到第一個事務結束。同一個事務中每一個更新請求都可能會鎖住一些記錄。
當多個事務同時需要對一些相同的記錄做更新操作時,就很有可能發(fā)生死鎖,例如:
Transaction 1, request 1, locks record 1 for update Transaction 2, request 1, locks record 2 for update Transaction 1, request 2, tries to lock record 2 for update. Transaction 2, request 2, tries to lock record 1 for update.
因為鎖發(fā)生在不同的請求中,并且對于一個事務來說不可能提前知道所有它需要的鎖,因此很難檢測和避免數(shù)據(jù)庫事務中的死鎖。
原文 Deadlock
譯者 申章
校對 丁一
via ifeve.com
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/17436.html
摘要:之前我們簡單的討論了一下關于中的同步還有一些鎖優(yōu)化的問題,今天我們就來簡單的聊一聊關于中的死鎖問題。這里顯示兩個線程的狀態(tài)現(xiàn)在是處于阻塞狀態(tài),然后都在等待鎖的獲取,我們再繼續(xù)往下看。 之前我們簡單的討論了一下關于Java中的同步還有一些鎖優(yōu)化的問題,今天我們就來簡單的聊一聊關于Java中的死鎖問題。 這個問題我們在開發(fā)的時候,或多或少都能遇到,對業(yè)務邏輯沒有正確的梳理,又或者是在多線程...
摘要:耐心看完的你或多或少會有收獲并發(fā)的核心就是包,而的核心是抽象隊列同步器,簡稱,一些鎖啊信號量啊循環(huán)屏障啊都是基于。 耐心看完的你或多或少會有收獲! Java并發(fā)的核心就是 java.util.concurrent 包,而 j.u.c 的核心是AbstractQueuedSynchronizer抽象隊列同步器,簡稱 AQS,一些鎖??!信號量??!循環(huán)屏障?。《际腔贏QS。而 AQS 又是...
摘要:此對象在線程受阻塞時被記錄,以允許監(jiān)視工具和診斷工具確定線程受阻塞的原因。阻塞當前線程,最長不超過納秒,返回條件在的基礎上增加了超時返回。喚醒線程喚醒處于阻塞狀態(tài)的線程。 LockSupport 用法簡介 LockSupport 和 CAS 是Java并發(fā)包中很多并發(fā)工具控制機制的基礎,它們底層其實都是依賴Unsafe實現(xiàn)。 LockSupport是用來創(chuàng)建鎖和其他同步類的基本線程阻塞...
摘要:并發(fā)需要解決的問題功能性問題線程同步面臨兩個問題,想象下有兩個線程在協(xié)作工作完成某項任務。鎖可用于規(guī)定一個臨界區(qū),同一時間臨界區(qū)內僅能由一個線程訪問。并發(fā)的數(shù)據(jù)結構線程安全的容器,如等。 并發(fā)指在宏觀上的同一時間內同時執(zhí)行多個任務。為了滿足這一需求,現(xiàn)代的操作系統(tǒng)都抽象出 線程 的概念,供上層應用使用。 這篇博文不打算詳細展開分析,而是對java并發(fā)中的概念和工具做一個梳理。沿著并發(fā)模...
閱讀 2753·2021-11-22 14:45
閱讀 896·2021-10-15 09:41
閱讀 1058·2021-09-27 13:35
閱讀 3662·2021-09-09 11:56
閱讀 2626·2019-08-30 13:03
閱讀 3191·2019-08-29 16:32
閱讀 3296·2019-08-26 13:49
閱讀 766·2019-08-26 10:35