java - 递归 ConcurrentHashMap.computeIfAbsent() 调用永远不会终止。错误或 "feature"?

标签 java recursion java-8 concurrenthashmap

前段时间,I've blogged about a Java 8 functional way of calculating fibonacci numbers recursively ,带有 ConcurrentHashMap 缓存和新的有用的 computeIfAbsent() 方法:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Test {
    static Map<Integer, Integer> cache = new ConcurrentHashMap<>();

    public static void main(String[] args) {
        System.out.println(
            "f(" + 8 + ") = " + fibonacci(8));
    }

    static int fibonacci(int i) {
        if (i == 0)
            return i;

        if (i == 1)
            return 1;

        return cache.computeIfAbsent(i, (key) -> {
            System.out.println(
                "Slow calculation of " + key);

            return fibonacci(i - 2) + fibonacci(i - 1);
        });
    }
}

我选择 ConcurrentHashMap 是因为我想通过引入并行性(我最终没有这样做)使这个示例更加复杂。

现在,让我们将数字从 8 增加到 25 并观察会发生什么:

        System.out.println(
            "f(" + 25 + ") = " + fibonacci(25));

程序永远不会停止。在方法内部,有一个永远运行的循环:

for (Node<K,V>[] tab = table;;) {
    // ...
}

我正在使用:

C:\Users\Lukas>java -version
java version "1.8.0_40-ea"
Java(TM) SE Runtime Environment (build 1.8.0_40-ea-b23)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

Matthias, a reader of that blog post also confirmed the issue (he actually found it) .

这很奇怪。我会期望以下两个中的任何一个:

  • 有效
  • 它抛出一个ConcurrentModificationException

但只是永不停止?这看起来很危险。这是一个错误吗?还是我误解了某些契约(Contract)?

最佳答案

这当然是一个“功能” ConcurrentHashMap.computeIfAbsent() Javadoc 显示:

If the specified key is not already associated with a value, attempts to compute its value using the given mapping function and enters it into this map unless null. The entire method invocation is performed atomically, so the function is applied at most once per key. Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this map.

“不得” 措辞是一个明确的约定,我的算法违反了这一约定,尽管不是出于相同的并发原因。

仍然有趣的是没有ConcurrentModificationException .相反,程序永远不会停止 - 在我看来这仍然是一个相当危险的错误(即 infinite loops. or: anything that can possibly go wrong, does )。

备注:

HashMap.computeIfAbsent() Map.computeIfAbsent() Javadoc 不禁止这种递归计算,这当然是荒谬的,因为缓存的类型是 Map<Integer, Integer>。 , 不是 ConcurrentHashMap<Integer, Integer> .子类型彻底重新定义父类(super class)型契约(Contract)是非常危险的(SetSortedSet 是问候语)。 因此也应该禁止在父类(super class)型中执行这种递归。

关于java - 递归 ConcurrentHashMap.computeIfAbsent() 调用永远不会终止。错误或 "feature"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44581783/

相关文章:

java - 自动连接列表中的 PK 和 FK

Java RMI - 访问不同机器上的 RMI 注册表对象 - AccessControlException?

python - "Unrolling"递归函数?

javascript - 为什么代码仍然运行到递归函数的末尾?

java - 如果我想在用 Java 上传之前压缩视频,我有什么选择?

java - 将父类(super class)的对象转换为子类——向下转型

java - 如何递归获取图形中可以包含循环的所有连接节点?

java - 在 MongoDB 中对文档使用 forEach

java - JDK文件夹下的jre文件夹和jre文件夹有什么区别?

JavaFX TextField 输入支持非英语语言