已有process
獲取兩個鎖:一個用於此對象,另一個用於下一個鎖。類似這樣的:
public void process() {
synchronized (this) {
synchronized (next) {
// manipulate the value of this and of next
}
}
}
這個答案應該會在你的腦海裏產生一個巨大的紅旗。 任何當你獲得多個鎖,你必須說服自己,他們不會死鎖!
只有兩個線程試圖以不同的順序獲取鎖時,纔會發生死鎖(即一個鎖A然後B鎖,另一個鎖鎖B然後鎖A)。在這種情況下,不可能發生 - 先前的對象始終在其下一個對象之前被鎖定。
因此,當調用array[0].process()
時,它鎖定array[0]
和array[1]
。當調用array[1].process()
時,它將嘗試鎖定array[1]
,這將會阻止,直到array[0].process()
完成。發生這種情況時,它將獲得array[1]
上的鎖定,然後嘗試獲取array[2]
上的鎖定。
雖然這仍然有可能導致死鎖。如果process
中的代碼激活一個新線程,然後嘗試在前一個對象上調用process
,然後嘗試加入該線程,則該線程會死鎖。例如,如果array[1].process()
這樣做,那麼該線程將嘗試獲取array[0]
和array[1]
的鎖。直到array[1].process()
完成,它將無法執行此操作,直到線程返回並因此死鎖纔會發生。
出於這個原因,你應該非常小心讓process
是「可插拔」(如,有人通過在接口或延伸的方法來確定操作是如何實現的。一般情況下,執行任何類型的「外部代碼」以這種方式在鎖中完成時是危險的。
但是,如果process
完全在您的控制之下,並且不旋轉任何線程,那麼您應該沒問題,因爲其中的鎖被收購。
在這樣的地方有一個有點微妙的舞蹈的情況下,它的情況並不少見無法獲取上this
的鎖,只是應景的自定義對象上:
public class A {
private final Object lock = new Object();
private int value;
private A next;
public void process() {
synchronized (lock) {
synchronized (next.lock) {
// etc
}
}
}
}
這可以保護你從別人進入並同步你的一個物體,並擾亂那個微妙的舞蹈。它還可以提供給任何正在閱讀代碼的人的視覺提醒,即有關同步的一些有趣的事情。