Java中的ConcurrentHashMap可以进行外部同步吗?

Fra*_*jas 6 java concurrency multithreading

我正在使用 ConcurrentHashMap,在计算尚不存在的新元素时需要迭代其所有元素,并可能对同一映射进行一些其他修改。

我希望这些操作是原子的,并阻止 ConcurrentHashMap 以防止出现源自并发的异常。

我编程的解决方案是将 ConcurrentHashMap 对象与其自身同步作为锁,但是 Sonar 报告了一个重大问题,所以我不知道该解决方案是否正确

建议的代码:

对原文的修改

public class MyClass<K, V> {
    ConcurrentHashMap<K, V> map = new ConcurrentHashMap<>();

    public V get(K key) {
        return map.computeIfAbsent(key, this::calculateNewElement);
    }

    protected V calculateNewElement(K key) {
        V result;
        // the following line throws the sonar issue:
        synchronized(map) {
            // calculation of the new element (assignating it to result)
            // with iterations over the whole map
            // and possibly with other modifications over the same map
        }
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码触发声纳主要问题:

多线程 - 在 util.concurrent 实例上执行同步

findbugs:JLM_JSR166_UTILCONCURRENT_MONITORENTER

此方法对作为 java.util.concurrent 包(或其子类)中的类的实例的对象执行同步。这些类的实例有自己的并发控制机制,这些机制与Java关键字synchronized提供的同步正交。例如,同步 AtomicBoolean 不会阻止其他线程修改 AtomicBoolean。

此类代码可能是正确的,但应仔细审查和记录,并且可能会使以后必须维护代码的人员感到困惑。

Nat*_*hes 5

如果您必须为每次更新更改许多节点,则可能您使用了错误的数据结构。查看树的并发实现。持久性集合(提供不变性和快速更新)似乎是理想的选择。

提供了一种用于原子更新的方法: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/ConcurrentHashMap.html#compute (K,java .util.function.BiFunction)

ConcurrentHashMap 的构建是为了允许高度并发访问。(请参阅这篇描述其内部工作原理的文章)如果使用提供的方法(例如compute、computeIfPresent等)对条目进行更新,则应仅锁定条目所在的段,而不是整个事物。

当您锁定整个地图进行更新时,您将无法从使用这种专门的数据结构中受益。这就是声纳所抱怨的。

还有一个问题是读者也必须进行锁定,更新线程并不是唯一需要锁定的线程。这就是 CHM 最初被发明的原因。