8

JCIPブックのリスト5.19Memorizerの最終的な実装。私の質問は次のとおりです。

  1. アトミックなputIfAbsent()のために、無限のwhileループがここにありますか?
  2. クライアントコードの代わりにputIfAbsent()のimplのすぐ内側でwhileループを実行する必要がありますか?
  3. whileループは、putIfAbsent()をラップするだけで、より小さなスコープに含める必要がありますか?
  4. whileループは読みやすさで悪いように見えます

コード:

public class Memorizer<A, V> implements Computable<A, V> {
    private final ConcurrentMap<A, Future<V>> cache
            = new ConcurrentHashMap<A, Future<V>>();
    private final Computable<A, V> c;
    public Memorizer(Computable<A, V> c) { this.c = c; }
    public V compute(final A arg) throws InterruptedException {
    while (true) { //<==== WHY?
        Future<V> f = cache.get(arg);
        if (f == null) {
           Callable<V> eval = new Callable<V>() {
               public V call() throws InterruptedException {
                    return c.compute(arg);
               }
           };
           FutureTask<V> ft = new FutureTask<V>(eval);
           f = cache.putIfAbsent(arg, ft);
           if (f == null) { f = ft; ft.run(); }
        }
        try {
           return f.get();
        } catch (CancellationException e) {
           cache.remove(arg, f);
        } catch (ExecutionException e) {
           throw launderThrowable(e.getCause());
        }
     }
   }
}
4

2 に答える 2

4

1)アトミックなputIfAbsent()のために、無限のwhileループがここにありますか?

ここでのwhileループは、計算がキャンセルされたときに計算を繰り返すためのものです(の最初のケースtry)。

2)クライアントコードの代わりにputIfAbsent()のimplのすぐ内側でwhileループを実行する必要がありますか?

いいえ、何をするか読んでくださいputIfAbsent。オブジェクトを1回だけ配置しようとします。

3)whileループは、putIfAbsent()をラップするだけで、より小さなスコープに含める必要がありますか?

いいえ、すべきではありません。#1を参照してください。

4)Whileループは読みやすさで悪いように見えます。

あなたはより良いものを自由に提供することができます。実際、この建設スイートは、成功するまで何かをしようとしなければならない状況に最適です。

于 2012-12-20T23:42:34.667 に答える
2

いいえ、whileループの範囲を縮小することはできません。f.get()キャッシュにある値に対して実行したい。マップにの値がない場合は、結果argに対して実行する必要があります。それ以外の場合は、マップとそのget()値の既存の値を取得する必要があります。argget()

問題は、この実装にはロックがないことです。したがって、値があるかどうかを確認してから値を挿入しようとすると、別のスレッドが独自の値を挿入した可能性があります。同様に、挿入が失敗してから取得するまでの間に、値がキャッシュから削除された可能性があります(のためにCancellationException)。これらの失敗の場合のためにwhile(true)、マップから正規の値を取得できるようになるか、マップに新しい値を挿入する(値を正規にする)まで、スピンインします。

f.get()ループの外に出ようとすることもできるように思われますが、試行を続けCancellationExceptionたい場所でのリスクがあるため、それは維持されます。

于 2012-12-20T22:45:50.717 に答える