3

系統中有兩個線程。一個是讀者線程,另一個是編寫器線程。Collections.synchronized映射是否使迭代器線程安全

使用以下代碼同步地圖。

Map<String,ArrayList<String>> m = Collections.synchronizedMap(new HashMap<String,ArrayList<String>()) 

讀者線程獲取映射值的Iterator,同時寫入器線程修改映射。

所以,我的問題是迭代器會拋出ConcurrentModificationException

+0

如何修改地圖?你沒有提到它。 – Bohemian

+0

那麼,我即將設計一個系統。我正在考慮使用ReadWriteLock。這就是爲什麼我想探索所有可能的場景。 – Touchstone

+0

您的問題中的代碼是100%線程安全的,因爲無法修改地圖 - 沒有直接引用地圖。 – Bohemian

回答

2

也許吧。這樣做並不安全。該documentation

當務之急是用戶迭代任何集合時返回的地圖上手動同步觀看

Collections.synchronized...使得單一的方法調用原子所以他們並不需要進一步的同步。但迭代不止是一個方法調用,所以需要額外的同步。下面是一個例子

Map<String, String> shared = Collections.synchronizedMap(new HashMap<>()); 

    new Thread(() -> { 
     while (true) { 
      synchronized (shared) { 
       for (String key : shared.keySet()) { 
        System.out.println(key); 
       } 
      } 
      try { 
       Thread.sleep(1000); 
      } catch (Exception e) { 
       break; 
      } 
     } 
    }).start(); 

    new Thread(() -> { 
     while (true) { 
      try { 
       // this is atomic 
       shared.put(UUID.randomUUID().toString(), "Yo!"); 
       Thread.sleep(1000); 
      } catch (Exception e) { 
       break; 
      } 
     } 
    }).start(); 
} 
+0

即可修改底層映射。因此,我想我需要使用同步來讀取和寫入對嗎? – Touchstone

+0

@Touchstone取決於你在寫作過程中所做的事情。單個方法調用否,如果多於一個需要是原子是的,請參閱編輯回答 – zapl

1

是的,Iterator仍可能拋出ConcurrentModificationException,因爲它不與同步相關(儘管它的名字所暗示的,以便)。該Iterator嘗試檢測在結構修飾(添加或對象的刪除)盡力而爲嘗試,而不管列表的操作是否同步。 一旦通過List.iterator() or List.listIterator()獲得迭代器,對列表(除了迭代器本身)所做的任何更改都將盡最大努力拋出CME異常。 可以確保ConcurrentModificationException唯一的辦法就是不甩 或者是通過讓您的閱讀器操作完成,然後再作家操作(反之亦然),或通過使用一個故障安全的Iterator從ConcurrentHashMap

Map hashmap = new HashMap<String,ArrayList<String>(); 
---- 

Map<String,ArrayList<String>> m = new ConcurrentHashMap<String,ArrayList<String>(hashmap)); 

的ConcurrentHashMap是一個故障安全迭代器,現在您可以專注於同步讀寫器操作,而不用擔心ConcurrentModificationException。

+0

所以,我想我需要使用同步的讀寫權利? – Touchstone

+0

只有同步不會足夠,您需要確保迭代器的操作不會互相交錯。或者,我建議您使用ConcurrentHashMap中的故障安全迭代器 –

+0

但是故障安全迭代器使用深度複製,這將導致髒讀!! – Touchstone