1 回答

TA貢獻(xiàn)1851條經(jīng)驗(yàn) 獲得超5個(gè)贊
考慮一家擁有數(shù)千個(gè)類(lèi)型的銀行賬戶(hù)的銀行Account?,F(xiàn)在讓我們看看為什么這段代碼會(huì)導(dǎo)致死鎖:
public void transfer(Account a,Account b,int amount)
{
synchronized(a)
{
a.withdraw(amount);
System.out.print(amount+" is withdrawn from account a\n");
try{Thread.sleep(500);}
catch(Exception e){System.out.println(e);}
synchronized(b)
{
b.deposit(amount);
System.out.print(amount+" is deposited into account b\n");
}
}
}
讓有線(xiàn)程tA
和線(xiàn)程tB
。如果線(xiàn)程在線(xiàn)程同時(shí)運(yùn)行時(shí)tA
運(yùn)行以下代碼 ,則可能會(huì)出現(xiàn)死鎖,因?yàn)槿绻覀儾榭匆韵马樞颍?code>transfer(accountA, AccountB)tB
transfer(accountB, accountA)
面向:
synchronized(accountA)
待定:
synchronized(accountB)
tA:嘗試鎖定對(duì)象
AccountB
,但鎖定由 tB 持有 =>死鎖
我們看到兩者之間存在循環(huán)依賴(lài)關(guān)系,不允許其中一個(gè)線(xiàn)程繼續(xù)前進(jìn)。
如果我們查看您更新的代碼:
public void transfer(Account a,Account b,int amount)
{
synchronized(a)
{
a.withdraw(amount);
System.out.print(amount+" is withdrawn from account a\n");
try{Thread.sleep(500);}
catch(Exception e){System.out.println(e);}
}
synchronized(b)
{
b.deposit(amount);
System.out.print(amount+" is deposited into account b\n");
}
}
我們必須采取以下假設(shè):
賬戶(hù) a 有無(wú)限的資金,因?yàn)樗赡苁?a.balance < amount,這意味著 a.balance < 0,這打破了我們總是有余額 >=0 的不變量。
我們?cè)试S不一致,例如,如果我們想?yún)R總所有當(dāng)前現(xiàn)金,我們將匯總少于實(shí)際金額,因?yàn)槟?dāng)前的代碼允許我們這樣做。
如果我們嘗試修復(fù)代碼,我們必須在更新余額之前確保 a.balance >= amount?,F(xiàn)在讓我們看看以下場(chǎng)景:
賬戶(hù)
a
有balance < amount
我們必須等到
a.balance >= amount
從帳戶(hù)中取款a
因?yàn)槲覀冊(cè)谶@個(gè)線(xiàn)程中保持 Account 的鎖
a
,所以沒(méi)有其他線(xiàn)程可以更新a.balance
=> 我們?cè)馐?strong>饑餓
要解決這些問(wèn)題,您要么必須使用監(jiān)視器或條件來(lái)檢查 a.balance>=amount 是否要進(jìn)行,并將線(xiàn)程置于阻塞狀態(tài),以便線(xiàn)程可以進(jìn)行或更新您的代碼,以便鎖定總是以相同的順序獲取。
解決方案#1:獲取對(duì)象鎖的唯一順序
如果我們使用唯一的順序獲取對(duì)象的鎖,我們可以確保不會(huì)發(fā)生死鎖,因?yàn)槲覀円灾付ǖ捻樞颢@取鎖,不允許任何循環(huán)依賴(lài),否則稱(chēng)為死鎖。
public void transfer(Account a,Account b,int amount)
{
//define a specific order, in which locks are acquired
//the id's of all accounts are unique!
if(a.id<b.id){
synchronized(a){
synchronized(b){
//do operations here
}
}
}else{
synchronized(b){
synchronized(a){
//do operations here
}
}
}
}
解決方案 #2:使用生產(chǎn)者-消費(fèi)者模式來(lái)檢查a.balance>=amount.
public void transfer(Account a,Account b,int amount)
{
while(true){
synchronized(a){
if(a.balance>=amount){
//do operations here
}
}
try{Thread.sleep(500);} //Use this as a backoff, as otherwise you'd likely get high congestion
catch(Exception e){System.out.println(e);}
}
synchronized(b)
{
//do operations here
}
}
添加回答
舉報(bào)