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

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

Collections.synchronizedMap(new LinkedHashMap());

虽然我收到了ConcurrentModificationException 错误。

【问题讨论】:

  • 你可以使用线程安全的哈希表
  • @user1479853 HashTable Java 社区不鼓励使用。
  • @user1479853 .... 也解决不了问题。

标签: java multithreading concurrency thread-safety


【解决方案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());。此处说明:

    正如 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,请不要重新发明轮子并使用经过验证和测试的框架,例如ehcachejboss 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 方法删除列表项,它可以在单线程环境中发生。

      同步只保证你串行访问。

      【讨论】:

        猜你喜欢
        • 2023-03-14
        • 1970-01-01
        • 2016-01-18
        • 2013-02-10
        • 1970-01-01
        • 2011-08-20
        • 2022-01-22
        • 1970-01-01
        • 2013-08-02
        相关资源
        最近更新 更多