Collections.synchronizedMap(new LinkedHashMap());没有使 Map 线程安全

Posted

技术标签:

【中文标题】Collections.synchronizedMap(new LinkedHashMap());没有使 Map 线程安全【英文标题】:Collections.synchronizedMap(new LinkedHashMap()); is not making Map threadsafe 【发布时间】:2012-10-20 12:15:54 【问题描述】:

我正在使用以下构造来创建线程安全 Map

Collections.synchronizedMap(new LinkedHashMap());

虽然我收到了ConcurrentModificationException 错误。

【问题讨论】:

你可以使用线程安全的哈希表 @user1479853 HashTable Java 社区不鼓励使用。 @user1479853 .... 也解决不了问题。 【参考方案1】:

没有代码很难猜出真正的问题是什么,但我的猜测是,您没有使用返回的集合来执行操作。根据javadoc

为了保证串行访问,对支持集合的所有访问都必须通过返回的集合完成。 用户在迭代返回的集合时必须手动同步它:

  Collection c = Collections.synchronizedCollection(myCollection);
     ...
  synchronized(c) 
      Iterator i = c.iterator(); // Must be in the synchronized block
      while (i.hasNext())
         foo(i.next());
  

不遵循此建议可能会导致不确定的行为。

【讨论】:

使用ConcurrentHashMap 代替Collections.synchronizedCollection(myCollection); 可以避免该问题。【参考方案2】:

不要贬低这里的任何其他答案,但下面的这段代码表明并发修改与实际的多线程几乎没有关系。这是因为您说迭代集合但在迭代时修改它......

  List list = new ArrayList();
  list.add("1");
  list.add("2");

  Iterator i = list.iterator();
  while (i.hasNext()) 
      Object value = i.next();  // throws java.util.ConcurrentModificationException

      list.add("another");  
  

【讨论】:

【参考方案3】:

长话短说,在您的代码中没有得到ConcurrentModificationException 的解决方案是使用ConcurrentHashMap 而不是Collections.synchronizedMap(new LinkedHashMap());。此处说明:

What's the difference between ConcurrentHashMap and Collections.synchronizedMap(Map)?

正如 Nambari 所说,如果没有实际代码,则更难识别问题。请注意,此Map 仅保护包含的对象。不过,您可以在方法中修改相同的对象实例:

Map<String, Object> map = new ConcurrentHashMap<String, Object();
//fill the map...
map.put("data", new Data());

//and now we have this unsynchronized method that two or more threads can access at the same time
public void modifyData(String key, int newDataIntValue) 
    //this is synchronized by the ConcurrentHashMap
    Data data = map.get(key);
    //this isn't
    //you can get problems here...
    data.setIntValue(newDataIntValue);

同步集合不会为这些情况保存您的代码。你应该自己同步这个方法。

附加信息:如果您正在尝试实现缓存库或Flyweight design pattern,请不要重新发明***并使用经过验证和测试的框架,例如ehcache 或jboss cache。

【讨论】:

【参考方案4】:

请查找 java 文档

返回由指定映射支持的同步(线程安全)映射。为了保证串行访问,对支持映射的所有访问都必须通过返回的映射来完成。 当迭代任何集合视图时,用户必须在返回的地图上手动同步:

  Map m = Collections.synchronizedMap(new HashMap());
  ...
  Set s = m.keySet();  // Needn't be in synchronized block
  ...
  synchronized(m)   // Synchronizing on m, not s!
      Iterator i = s.iterator(); // Must be in synchronized block
       while (i.hasNext())
          foo(i.next());
   

不遵循此建议可能会导致不确定的行为。 如果指定的地图是可序列化的,则返回的地图将是可序列化的。

参数: m 将地图“包装”在同步地图中。 回报: 指定地图的同步视图。

【讨论】:

您的同步不适用于同时访问此方法的 2 个线程。【参考方案5】:

Synchronized 与 ConcurrentModificationException 无关,因为如果您在迭代列表时尝试使用列表的 remove 方法删除列表项,它可以在单线程环境中发生。

同步只保证你串行访问。

【讨论】:

以上是关于Collections.synchronizedMap(new LinkedHashMap());没有使 Map 线程安全的主要内容,如果未能解决你的问题,请参考以下文章