1. ホーム
  2. java

再帰的な ConcurrentHashMap.computeIfAbsent() の呼び出しが終了しない。バグ?それとも「機能」?

2023-10-01 06:48:59

質問

少し前のことですが フィボナッチ数を再帰的に計算するJava 8の関数的な方法についてブログで書きました。 を使用した 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 氏も問題を確認しました (彼は実際にそれを見つけました)。 .

これは奇妙なことです。私は次の2つのうちどれかを期待していたのですが。

  • それは動作します
  • を投げます。 ConcurrentModificationException

でも、決して止まらないだけ?それは危険なように思えます。バグなのでしょうか?それとも、私が何かの契約を誤解していたのでしょうか?

どのように解決するのですか?

これは JDK-8062841 .

での 2011年提案 , コードレビューの際にこの問題を確認しました。JavaDocが更新され、一時的な修正が追加されました。パフォーマンス上の問題から、さらなる書き換えで削除されました。

での 2014 年の議論 で、私たちはより良い検出と失敗の方法を探りました。なお、低レベルの変更を検討するために、議論の一部はオフラインで私的な電子メールに移行しました。すべてのケースをカバーできるわけではありませんが、一般的なケースはライブロックしません。これらの の修正 はDougのリポジトリにありますが、JDKのリリースには至っていません。