我還沒有讀過這本書,但我認爲他們想說的是每個Vector
方法都是同步的(獨立的),即使這可以保護Vector
免受損壞,但它不會保護它可能存儲在其中的信息(尤其是因爲業務規則,數據模型,數據結構設計或您想要調用它們)。
示例:方法transfer
是天真地執行,相信如果它Vector
的set
方法是同步的,那麼一切都很好。
public void transfer(Vector<Double> accounts, int from, int to, int amount) {
accounts.set(from, accounts.get(from) - amount);
accounts.set(to, accounts.get(to) + amount);
}
如果2個線程(T2和T3)調用transfer
在同一時間,比方說,同樣的from
帳戶(A1)和平衡$ 1,500不同to
賬戶(A2和A3)會發生什麼用餘額$ 0?說出100美元到A2和1200美元到A3?
- T2:
accounts.get('A1')
被檢索並減去100。結果= 1,400,但它的尚未儲存回去。
- T3:檢索
accounts.get('A1')
(仍爲1,500)並減去1,200。結果= 300
- T3:將結果存回:
accounts.get('A1')
(如果執行)將生成300.
- T2:存儲以前計算的結果。如果執行,
accounts.get('A1')
將產生1,400。
- T2:
accounts.get('A2')
被檢索(值0),100被添加並被存回。 accounts.get('A2')
(如果執行)將產生100.
- T3:
accounts.get('A3')
被檢索(值0),1,200被添加並被存回。 accounts.get('A3')
(如果執行)將產生1200。
於是,我們開始A1
+ A2
+ A3
= 1500 + 0 + 0 = 1500,而做這些內部轉賬後,我們有A1
+ A2
+ A3
= 1400 + 100 + 1200 = 2,700元。顯然有些東西在這裏不起作用。什麼?在步驟1和步驟4之間.T2保持A1
的平衡,並且沒有(不能)檢測到T3
也在減少它,至少在概念層面。
這當然不會發生在每次運行中。但這是邪惡的,因爲如果隱藏在幾十萬行代碼中,再現(以及發現和重新測試)這個問題將會很困難。
但是請注意,即使accounts
方法從未同時被調用,上述情況也會發生。甚至沒有嘗試。實際上,accounts
作爲Vector
沒有損壞,但它作爲我們的數據結構已損壞。
這就是那句
get和Vector類的設置方法是同步的,但這並不能幫助我們。
指的是。
如果說對於提出的解決方案是
這種方法的工作原理,但它是完全依賴於Vector類使用其所有mutator方法的內部鎖的事實。
,作者肯定假設除了轉移方法之外,account
還有其他用途。例如:添加賬戶,刪除賬戶等。從這個意義上說,幸運的是,這些方法也在矢量上同步。如果它們在一個內部對象中同步,則系統的開發人員需要將accounts
包裝在輔助同步層中,這次是基於accounts
本身或任何其他常見對象。
最後,關於
如果一個線程A擁有的賬戶鎖定,沒有其他線程可以獲取同樣的鎖。它不依賴於Vector用於其增變器方法的鎖。
這可能恰恰是這樣一個觀點:所有需要對數據結構進行互斥訪問的類需要就他們將要訪問的對象達成一致synchronize
。這個案例非常簡單。在更復雜的情況下,選擇這個對象不是微不足道的。在許多情況下,相反,如果將某個對象放在其他對象上,則會創建一個特殊的「鎖定」對象。但是這有一個限制:整個數據結構一次只能更新一次。在出現這種問題的應用程序中,需要定義更詳細的鎖定策略,開發人員可能難以確定哪些對象可能被鎖定,以及哪些對象應鎖定在每種可能的情況下。這些策略還需要關注死鎖和競態條件的可能性。
「如果向量決定改變它的鎖定機制」雖然這是不可能的。它如何同步顯然是API的一部分,他們不會打破這一點。 – Thilo
在Vector的Javadoc中,它提到了你可以依賴所有被同步的方法。如果將'Vector'切換到使用內部的'private final Object lock',它們不會相同嗎?我知道他們不會改變它,但這是我反對的模式。 @Thilo。 – Gray
@Thilo實際上,他們在書中進一步提到Vector並未做出這樣的承諾。 –