2017-02-21 45 views
-1

如果我們不使用Collections.synchronizedMap()並且假設我有一個多線程環境可以散列表在多線程環境中有重複鍵

我知道競爭條件,大小調整問題等

我的問題是可以有這樣的情況,2個線程TaTb具有相同的對象,並試圖放入地圖。

是否可以有2個條目,如果不是如何防止的話。在兩個不同線程同時運行的2個調用之間是否有一小部分時間差異。

據我瞭解,對於TaTb都會在放置前檢查,所以這裏是否有重複鍵的情況。

考慮到我們已經適當地覆蓋了hashcodeequals

+0

我認爲他們總是會互相重疊。如果你沒有正確地同步它,你可能會得到一個concurrentexception。 –

+0

你爲什麼要問?你肯定不打算在這種情況下使用非線程安全的容器? –

+0

因爲_both會在puts_之前檢查,你有你的競爭條件:插入不是原子(一個不可分割的)操作,但它至少包含2個操作,當'Ta'與'Tb' 。 –

回答

3

的Javadoc HashMap狀態:

注意,此實現不是同步的。如果 同時訪問哈希映射多個線程和線程 中的至少一個修改了該映射結構,它必須外部同步。 (A 結構修改是添加或刪除一個或更多映射的任何操作;僅更改與實例已包含的密鑰關聯的值不是結構修改。)這通常是通過同步某個對象自然 封裝了地圖。如果不存在此類對象,則應使用Collections.synchronizedMap方法將地圖「包裝」爲 。

所以文檔說,你必須莫名其妙同步訪問,但不要說如果不這樣做會發生什麼。這意味着你這樣做的行爲是undefined - 所有投注都關閉。

你可以看一下the source code for HashMap自己。的put心臟是:

 for (Entry<K,V> e = table[i]; e != null; e = e.next) { 
     Object k; 
     if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 
      V oldValue = e.value; 
      e.value = value; 
      e.recordAccess(this); 
      return oldValue; 
     } 
    } 

    modCount++; 
    addEntry(hash, key, value, i); 
    return null; 

(編輯 - 這是Java 6的Java實現8位的是截然不同的 - 這增強了點)

我們可以推測的結果,如果兩個線程試圖這同時 - 但很難推理。有時它會導致兩個輸入具有相同的密鑰,有時不會。這取決於時間。

TreeMapput()當然是完全不同的,它的怪癖當以這種方式濫用時會有所不同。

任何這樣的行爲是執行的怪癖,並且實現可能在未來沒有警告改變,因爲我們正在談論不確定的行爲。實施不作任何承諾,你說不會:

  • 自動刪除條目
  • 進入無限循環
  • NullPointerException
  • 要求大量的內存
  • 損壞存儲,以便與其他鍵的條目丟失
  • 使先前刪除的條目重新出現
  • 創建包含垃圾堆m埃默裏

該文檔狀態,從其他地方的修改,而一個Iterator工作的對象,會導致Iterator拋出一個ConcurrentModificationException - 但是這是從不同的關注同步,並且如果您使用了一個SynchronizedMap

總而言之,不要這樣做。