【问题标题】:Does a ConcurrentHashMap need to be wrapped in a synchronized block?ConcurrentHashMap 是否需要包装在同步块中?
【发布时间】:2014-11-17 20:42:21
【问题描述】:

ConcurrentHashMap(put()remove() 等)上的所有非检索操作是否需要包装在 synchronized(this) 块中?我知道所有这些操作都是线程安全的,那么这样做有什么真正的好处/需要吗?唯一使用的操作是put()remove()

protected final Map<String, String> mapDataStore = new ConcurrentHashMap<String, String>();

public void updateDataStore(final String key, final String value) {
    ...
    synchronized (this) {
        mapDataStore.put(key, value);
    }
    ...
}

【问题讨论】:

    标签: java synchronized concurrenthashmap


    【解决方案1】:

    不,这样做会失去ConcurrentHashMap 的好处。您也可以使用HashMapsynchronizedsynchronizedMap() 来锁定整个表(这是您在synchronized 中包装操作时所做的,因为隐含的监视器是整个对象实例。)

    ConcurrentHashMap 的目的是通过允许对表进行并发读/写而不锁定整个表来增加并发代码的吞吐量。该表通过使用锁条带在内部支持这一点(多个锁而不是一个,每个锁分配给一组哈希桶 - 参见 Goetz 等人的Java Concurrency in Practice)。

    一旦您使用ConcurrentHashMap,所有标准映射方法(put()remove() 等)都将由于实现中的锁条带化等而成为原子方法。唯一的权衡是size()isEmpty() 之类的方法不一定会返回准确的结果,因为它们的唯一方法是让所有操作锁定整个表。

    ConcurrentMap interface 接口还添加了新的原子复合操作,例如 putIfAbsent()(仅当键不在映射中时才放置内容),remove() 同时接受键和值(仅当其值时删除条目等于您传递的参数)等。这些操作过去需要锁定整个表,因为它们需要两个方法调用才能完成(例如,putIfAbsent() 需要同时调用containsKey()put(),包裹在一个synchronized 中块,如果您使用的是标准的 Map 实现。)再一次,通过避免锁定整个表,您可以使用这些方法获得更大的吞吐量。

    【讨论】:

      【解决方案2】:

      同步这些操作在这里没有任何好处 - 如果您不需要同步,它实际上会降低性能。

      创建ConcurrentHashMap 的原因是同步映射(在问题中手动实现或使用Collections.synchronizedMap(map) 以通常的方式实例化)在被许多线程访问时表现出糟糕的性能。 put 和 get 操作是阻塞的,所以所有其他线程都必须等待并且不能同时访问 map。 ConcurrentHashMap - 顾名思义 - 另一方面允许并发访问。如果添加同步,您将失去此优势。

      【讨论】:

      • 如果代码块同时包含 containsKey() 和 put() 方法怎么办?是否应该同步?
      • @gaurav 这应该是同步的。但我宁愿使用 putIfAbsentcomputeIfAbsent 方法(它们都是 ConcurrentMap 的原子操作),而不是同步。
      • 2:如果使用 Java 1.7 会有什么选择?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多