[英]How Copy-On-Write is different from a direct lock / synchronized on write method?
寫時復制被認為是並發方案中的良好實踐之一。 但是,我不清楚它與寫方法上的簡單鎖/同步有何不同。 有人可以幫忙解釋一下嗎?
寫時復制:
public V put(K key, V value) {
synchronized (this) {
Map<K, V> newMap = new HashMap<K, V>(internalMap);
V val = newMap.put(key, value);
internalMap = newMap;
return val;
}
}
直接鎖定/同步:
public V put(K key, V value) {
synchronized (this) {
internalMap.put(key, value);
}
}
對於寫線程,在上面的兩個示例中,它們相互排斥,相同。
對於讀取線程,在“寫時復制”中,運行“ internalMap = newMap”后的讀取操作將獲取更新后的值。 並且在直接鎖定中,運行“ internalMap.put(key,value)”后的讀取動作將獲得更新后的值。
那么,為什么我們要推廣寫時復制? 為什么我們在寫時必須“復制”?
此示例的一個好處是,您可以獲得寫時復制的快照語義:對internalMap
每個引用都是不可變的,一旦獲得,就不會再更改。 當您有許多並發的讀取操作遍歷internalMap
且僅偶爾進行更新時,這可能會很有用。
使用鎖定和寫時復制都可以(實際上)實現相同的功能。 他們中的任何一個都沒有天生比另一個更好。
通常,在有很多讀取但很少寫入的情況下,寫時復制性能會更好。 這是因為平均而言,讀取比使用鎖便宜,而由於復制,寫入更昂貴。 當您進行大量寫操作時,通常最好使用鎖。
為什么寫操作更昂貴的原因很明顯(您必須在每次寫操作時都復制整個映射,,)。 讀取便宜的原因如下:
volatile Map<K, V> internalMap = new HashMap<>();
讀取internalMap不需要獲取鎖(有關更多詳細信息,請參見Java中volatile和sync之間的區別 )。 一旦線程獲得了對internalMap
的引用,它們就可以繼續處理該副本(例如,遍歷條目),而無需與其他線程進行協調,因為可以保證它不會被突變。 盡可能多的線程可以處理單個映射副本(快照)。
為了進行類比解釋,請想象一個作者正在起草一篇文章,並且有一些人作為事實檢查者。 使用鎖時,其中只有一個可以處理草稿。 使用寫時復制,作者將一個不變的快照(副本)發布到某個地方,事實檢查人員可以抓取並完成他們的工作-在工作時,他們可以根據需要讀取快照(而不是每次都中斷作者)忘記了文章的某些部分等)。
多年來,Java的鎖已得到改進,因此差異很小,但是在極端情況下,不必獲取鎖/不必在線程之間進行協調會導致更高的吞吐量等。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.