ConcurrentHashMap 是否需要包装在同步块中?

2022-09-01 01:25:48

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);
    }
    ...
}

答案 1

不,这样做会给您带来的好处。您也可以使用 with 或 来锁定整个表(这是在 中包装操作时执行的操作,因为监视器隐含的是整个对象实例。ConcurrentHashMapHashMapsynchronizedsynchronizedMap()synchronized

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

一旦你使用,所有标准的映射方法(,等)都通过实现中的锁条带化等而成为原子。唯一的权衡是,方法喜欢并且不一定返回准确的结果,因为它们的唯一方法是让所有操作锁定整个表。ConcurrentHashMapput()remove()size()isEmpty()

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


答案 2

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

创建的原因是,同步映射(要么像在问题中那样手动实现,要么以通常的方式实例化),当被许多线程访问时,性能会很差。Put 和 get 操作被阻塞,因此所有其他线程都必须等待,并且无法同时访问映射。顾名思义,另一方面允许并发访问。如果添加同步,则会失去此权益。ConcurrentHashMapCollections.synchronizedMap(map)ConcurrentHashMap


推荐