簡體   English   中英

線程鎖定順序與簡單的同步塊

[英]Thread lock ordering versus a simple synchronized block

在閱讀Java Concurrency in Practice一書時,遇到了這段代碼,其中“fromAccount”和“toAccount”對象被一個接一個地鎖定,以防止動態鎖定命令死鎖。

public void transferMoney(Account fromAccount,Account toAccount) {
    **synchronized (fromAccount) {**
        **synchronized (toAccount) {**
               ........
        }
    }
}

我很困惑為什么需要這種鎖定順序。如果我們只是想確保兩個對象同時被鎖定,那么如果只有一個常規的同步塊,你就不會得到相同的效果訪問fromAccount和toAccount對象。 我相信我在這里錯過了一些基本概念。 謝謝您的幫助。

public void transferMoney(Account fromAccount,Account toAccount) {
    synchronized (this) {
        fromAccount.someMethod();
        toAccount.someMethod();        
    }
}

你想要避免使用鎖定順序示例的替代方法:擁有一個中央鎖,一切都在使用,因為那時你沒有得到並發傳輸,一切都等待一個鎖,一次只能進行一次傳輸。 目前尚不清楚this是什么或它的范圍可能是什么,但如果有這種轉移服務的多個實例,那么鎖定沒有任何好處,因為涉及一個帳戶的一個轉移可以通過一個實例而另一個涉及該轉移的轉移帳戶可以通過另一個。 因此,似乎只能存在其中一個,這會使您的並發性一次性減少到一次傳輸。 你不會死鎖,但你也不會很快處理很多轉移。

這個玩具示例背后的想法(你不應該誤解任何人如何轉移資金)是因為它試圖通過鎖定轉移中涉及的個人賬戶來獲得更好的並發性,因為對於大量的轉移,所涉及的賬戶都不是參與其他並發傳輸,您希望能夠同時處理它們,並通過最小化鎖定到各個帳戶的鎖定范圍來最大化並發性。 但是,如果某個帳戶涉及多個並發傳輸,並且對於某些傳輸以不同的順序獲取鎖,則此方案會遇到麻煩。

首先,應該注意的是你帶來的例子(基於你的評論,它是第208頁,列出10.2)是一個糟糕的例子 - 一個以死鎖結束的例子。 對象不會一個接一個地鎖定以防止動態鎖定順序死鎖,它們是動態鎖定順序發生的示例!

現在,你的建議鎖定在this ,但是這是什么this反正,什么是鎖定的范圍是什么?

  • 很明顯,同一個對象必須用於所有操作 - 撤銷,存款,轉移。 如果使用單獨的對象,則一個線程可以在賬戶A上存款,而另一個線程從賬戶A轉賬到賬戶B,並且他們將不會使用相同的鎖定,因此余額將受到損害。 因此,對同一帳戶的所有訪問的鎖定對象應該是相同的。
  • 正如Nathan Hughes解釋的那樣,人們需要將鎖定本地化 我們不能為所有帳戶使用一個中央鎖定對象,或者我們將讓它們全部等待彼此,盡管實際上並沒有使用相同的資源。 因此,使用中央鎖定對象也是不可能的。

所以看來我們需要本地化鎖,以便每個帳戶的余額都有自己的鎖,以便允許不相關帳戶之間的並行操作,但是這個鎖必須用於所有操作 - 撤銷,存入和轉移。

問題就出現了 - 當它只是撤銷或存款時,您只在一個帳戶上操作,因此您只需鎖定該帳戶即可。 但是當你轉移時 ,你有兩個對象。 所以你需要鎖定它們的余額,以防有其他線程想要操作。

對兩個或多個帳戶持有單個鎖的任何對象都將破壞上述兩點之一。 要么它不會用於所有操作,要么它不會足夠本地化。

這就是他們試圖一個接一個地鎖定兩個鎖的原因。 他們的解決方案是使Account對象本身成為Account的鎖定 - 它滿足“所有操作”條件和“位置”條件。 但在我們轉賬之前,我們仍然需要確保我們擁有兩個賬戶的鎖。


但同樣,這個源代碼是一個容易出現死鎖的代碼 這是因為一個線程可能想要從帳戶A轉移到帳戶B,而另一個線程想要從帳戶B轉移到帳戶A.在這種情況下,第一個鎖定A帳戶,第二個鎖定B帳戶,然后他們陷入僵局,因為他們以相反的順序執行了鎖定。

這里的基本原則是避免競爭條件 在您的情況下,如果在任何其他類中還有另一種方法也在轉賬到賬戶,那么不正確的金額可能會在toAccount中更新。 例如,有2個班級進行匯款。

一個類有一個方法:

public void transferMoney(Account fromAccount,Account toAccount) {
    synchronized (this) {
        fromAccount.someMethod();
        toAccount.someMethod();        
    }
}

和其他類包含:

public void transferMoneyNow(Account fromAccount1,Account toAccount) {
    synchronized (this) {
        fromAccount1.someMethod();
        toAccount.someMethod();        
    }
}

如果兩種方法同時發生,由於競爭條件不正確,金額可能會更新到帳戶。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM