2012-09-14 20 views
4

我最近在代碼庫上遇到了一個類,它擴展了HashMap並同步了put方法。擴展HashMap <K,V>並只同步放入

除了它比使用ConcurrentHashMap的效率較低,可延伸的HashMap,只有同步放出現什麼樣的問題(K,V)?

假設我們不在乎get(K)是否返回最新值(例如,我們很好,線程覆蓋對方,我們不關心可能出現的競爭條件,如果該地圖本身被用作鎖)。

例子:

public class MyMap<K,V> extends HashMap<K,V> { 
    //... 
    public synchronized void put(K key, V value) { 
     //... 
    } 

    //... 
} 

據我所知,HashMap中做了重新的大小與put方法和自投放在地圖實例級同步,同時重新上漿過程中遇到的問題會(可能)不會遇到。

即使有上述可疑的假設,我的直覺告訴我可能會出現更多問題。還是我只是偏執狂?

更新:謝謝大家,這很有趣和有啓發性。如果我見過這個班級作者的原作,我現在可以詳細解釋他的愚蠢。 :)

總結: putAll仍然可怕地搞砸了數據結構,並最終在可怕的無限循環/數據競爭條件。 獲取依賴於hashmap的底層內部數據結構,這些數據結構可能正在被併發修改,導致get過程異常行爲。 這只是一個普遍不好的主意。至少,作者可以使用Collections.synchronizedMap(Map)來代替。

注:鑑於在寫這篇文章的所有三個答案其實是正確的,但我挑了約的get()爲正確答案之一,因爲它是最明顯的一個我。

+0

但哈希表是這樣同步的。 –

+0

哈希表完全同步(放入並獲取)。 –

+0

所以,synched gets會讓它變得更慢 –

回答

3

因爲get()可以讀取變化的數據結構,一切不好的可能發生。

我見過get()被困在死循環中,所以它不僅僅是理論上的可能性,而且壞事情也會發生。

+0

如上所述,這是我的直覺告訴我的,但考慮到我的問題中所陳述的假設(不需要可視性保證,地圖上存儲的值不會被當作自己的鎖使用,我們可以確信值會被覆蓋),什麼是可能出錯的事情? – Hyangelo

+2

@Hyangelo - 你是誤會。除了值的(缺失)可見性保證之外,不保證_HashMap_內部的任何一致性。例如,您可以看到一個內部表格,其中包含空值。或任何其他內部對象可能有虛假的空值,等等...... – jtahlborn

+1

@Hyangelo - 還要注意,值本身的可見性問題不是_just_看到陳舊值的可能性,它還意味着值對象的內部狀態可能會被搞砸(除非備用同步/易失性用於其內部狀態)。 – jtahlborn

5

我希望你也同步上putAllremove。特別是因爲多個線程可以嘗試調整你的HashMap的大小。這些方法也將更新sizemodCount,如果在同步之外完成,可能會導致更新丟失。

+0

當併發調整大小發生時,不會提及可能的數據結構損壞! – Hyangelo

+0

是的!我最喜歡的數據競賽。 –

2

正如我在評論所提到的,可能出現的另一個問題是,putAll(Map)方法不會出現同步。由於putAll也可以修改Map的結構,因此在另一個線程使用相同的Map時,將其稱爲不同步於一個線程是不安全的。

雖然在更高層次,但理解更多爲什麼爲什麼put(key, value)是​​會很有趣。即使你已經防範了對地圖結構的不同步修改,但對於多個線程在沒有同步的情況下訪問地圖仍然不是一個好主意。實際上,如果線程A試圖遍歷HashMap的內容,並且線程B調用synchronized put(key, value),則線程A中的迭代器仍然會快速失敗並拋出ConcurrentModificationException,而不是執行某些非確定性操作。

即使您要同步putAll(Map)調用,其他線程遍歷地圖內容仍會看到異常。如果Map需要在多個線程中使用,並且這些線程中至少有一個需要修改Map,則所有的調用都需要同步。

+0

雖然解析點與'Collections.synchronizedMap'相同,但'ConcurrentModificationException'上的好點 –