Java 8 JDK 8:ConcurrentHashMap.compute似乎偶尔允许多次调用重新映射函数

Java 8 JDK 8:ConcurrentHashMap.compute似乎偶尔允许多次调用重新映射函数,java-8,concurrenthashmap,atomicity,Java 8,Concurrenthashmap,Atomicity,我正在开发一个高度并发的应用程序,它使用基于ConcurrentHashMap的对象缓存。我对ConcurrentHashMap的理解是,调用“compute”方法家族可以保证重映射函数的原子性。然而,我发现了一些异常行为:偶尔会多次调用重映射函数 我的代码中的以下代码片段显示了这是如何发生的,以及我必须做些什么来解决这一问题: private ConcurrentMap<Integer, Object> cachedObjects = new Concurrent

我正在开发一个高度并发的应用程序,它使用基于
ConcurrentHashMap
的对象缓存。我对ConcurrentHashMap的理解是,调用“compute”方法家族可以保证重映射函数的原子性。然而,我发现了一些异常行为:偶尔会多次调用重映射函数

我的代码中的以下代码片段显示了这是如何发生的,以及我必须做些什么来解决这一问题:

private ConcurrentMap<Integer, Object> cachedObjects 
        = new ConcurrentHashMap<>(100000);

private ReadWriteLock externalLock = new ReentrantReadWriteLock();
private Lock visibilityLock = externalLock.readLock();

...

public void update(...) {
    ...
    Reference<Integer> lockCount = new Reference<>(0);
    try {
        newStats = cachedObjects.compute(objectId, (key, currentStats) -> {

            ...

            visibilityLock.lock();
            lockCount.set(lockCount.get() + 1);
            return updateFunction.apply(objectId, currentStats);
        });
    } finally {
        int count = lockCount.get();
        if (count > 1) {
            logger.debug("NOTE! visibilityLock acquired {} times!", count);
        }
        while (count-- > 0) {
            // if locked, then unlock. The unlock is outside the compute to
            // ensure the lock is released only after the modification is
            // visible to an iterator created from the active objects hashmap.
            visibilityLock.unlock();
        }
    }
    ...
}

关注JDK代码:

很明显,compute不会两次调用
remappingFunction
。这就留下了三种可能性:

  • 您没有一个
    ConcurrentHashMap
    ,但有一些其他的东西。在运行时检查具体类型并将其转储到日志文件
  • 您正在调用
    compute
    两次。函数中是否存在任何可能未达到预期效果的流量控制?你已经删除了大部分函数,所以我不可能这么说
  • 您正在调用
    compute
    一次,但是
    remappingFunction
    锁定了两次。lambda中是否有任何流量控制可能不符合您的想法?同样,您已经删除了大部分函数,因此我无法提供任何帮助

要进行调试,请在锁定点检查锁计数,如果它不为零,则将堆栈转储到日志文件。

您试图用concurrentmap尚未完成的
visibilityLock实现什么,
visibilityLock
的类型是什么?谢谢@Ben-我忘了提到
visibilityLock
的类型-我已经相应地更新了文本-以及我们为什么需要它的信息。需要它的原因在代码片段中并不明显——只需说我们需要。我明白了。我的第一个建议是通过对具体类型进行运行时类型检查来验证您对ConcurrentHashMap的看法,并检查您的JDK版本和文档是否具有相同的原子性保证。@SeanBright-请注意,
lockCount
是一个局部变量。因此,不存在通过多线程的争用。如果当我们点击
finally
块时
lockCount
大于1,这只能是因为对
compute
的单个调用多次调用了重映射函数(lambda)。还要注意,该锁是读写器锁中的读写器锁。因此,多个线程可以锁定此锁。关于
lockCount
的唯一问题是在对
compute
的一次调用中发生了多少次这种情况(因此我不会留下一个未完成的读卡器锁)。
$ java -version
openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)