【问题标题】:Java synchronized block vs concurrentHashMap vs Collections.synchronizedMapJava 同步块 vs concurrentHashMap vs Collections.synchronizedMap
【发布时间】:2012-09-27 19:20:08
【问题描述】:

如果有一个同步方法,并且在该方法中,我会像这样更新一个哈希图:

public synchronized void method1()
{
    myHashMap.clear();
    //populate the hashmap, takes about 5 seconds.
}

现在当method1正在运行并重新填充hashmap时,如果有其他线程试图获取hashmap的值,我假设它们会被阻塞?

现在不使用同步方法,如果我将 hashmap 更改为 ConcurrentHashMap,如下所示,会有什么行为?

public void method1()
{
     myConcurrentHashMap.clear();
    //populate the hashmap, takes about 5 seconds.
}

如果我使用 Collections.synchronizedMap 会怎样?是一样的吗?

【问题讨论】:

    标签: java synchronization


    【解决方案1】:

    CHM(ConcurrentHashMap),而不是在一个普通锁上同步每个方法,限制对单个线程的访问 一次,它使用称为锁条带的更细粒度的锁定机制来允许更大程度的共享访问。任意多的阅读线程 可以同时访问地图,读者可以同时访问地图 writers,并且有限数量的 writers 可以同时修改映射。结果 并发访问下的吞吐量要高得多,性能损失很小 单线程访问。 ConcurrentHashMap 与其他并发集合一起,进一步改进 通过提供不抛出的迭代器来同步集合类 ConcurrentModificationException,从而无需锁定集合 在迭代期间。

    与所有改进一样,仍有一些权衡。 方法的语义 对整个 Map 进行操作的 size 和 isEmpty 等 弱化以反映集合的并发性。由于尺寸的结果 可能在计算时已经过时,它实际上只是一个估计值,所以大小 允许返回近似值而不是精确计数。虽然起初这 可能看起来令人不安,实际上像 size 和 isEmpty 这样的方法在 并发环境,因为这些数量是移动的目标。



    其次,Collections.synchronizedMap

    这只是简单的带有同步方法的 HashMap - 我称它为 CHM 已弃用

    【讨论】:

      【解决方案2】:

      如果你想让你的HashMap同步所有读写操作,你需要把synchronize放在所有访问HashMap的方法上;仅仅阻止一种方法是不够的。

      ConcurrentHashMap 允许线程安全地访问您的数据而无需锁定。这意味着您可以在一个线程中添加/删除值,同时在另一个线程中获取值而不会遇到异常。另见documentation of ConcurrentHashMap

      【讨论】:

      • 如果我的另一个线程正在尝试获取该哈希图中的所有值,而不仅仅是地图的某些值,它是如何工作的?
      • 您必须为所有其他线程阻塞 HashMap。这意味着您不仅必须在从地图中检索值的方法上加上synchronized,而且还必须在读取修改地图的所有其他方法上加上synchronized。否则,当您遍历地图时,您可能会得到 ConcurrentModificationException。您可以通过调用 entrySet() 方法并遍历结果集来获取地图的所有条目。
      • @Anna 如果使用Collections.synchronizedMap,您没有说明情况。
      【解决方案3】:

      你可能会这样做

      volatile private HashMap map = newMap();
      
      private HashMap newMap() {
          HashMap map = new HashMap();
          //populate the hashmap, takes about 5 seconds
          return map;
      }
      
      public void updateMap() {
          map = newMap();
      }
      

      读取器看到一个常量映射,因此读取不需要同步,也不会被阻塞。

      【讨论】:

      • 这很有趣。通过使用 volatile,他们是否总能看到部分的“完整”地图?
      • 您创建一个完整的地图,然后将其分配给 volatile 变量。之后,读者可以看到整个地图。
      • volatile 只处理线程间映射引用的可见性。如果要由不同的线程同时安全地访问地图,则上面的代码确实需要同步。正如问题所述,这与同步集合包装器或 ConcurrentHashMap 没有类似的效果。
      猜你喜欢
      • 2010-10-08
      • 1970-01-01
      • 1970-01-01
      • 2012-05-08
      • 1970-01-01
      • 1970-01-01
      • 2014-10-04
      • 2023-04-04
      • 1970-01-01
      相关资源
      最近更新 更多