在Brian Goetz的Java Concurrency In Practice中,为什么在Memoizer中检查了两次(f == null)

时间:2018-01-28 03:54:46

标签: java concurrency thread-safety java.util.concurrent concurrenthashmap

实践中的Java并发实践Brian Goetz提供了一个用于并发使用的高效可伸缩缓存的示例。显示类Memoizer(第108页)实现的示例的最终版本显示了这样的缓存。我想知道为什么有一个内部和外部检查if(f == null)。 第二个没有任何意义,因为:

  1. 前面有一个检查,前面的最后一步肯定会从cache.putIfAbsent(arg, 英尺);
  2. 第二次检查中的ft.run()没有任何意义,因为此后会立即调用f.get()。
  3. 以下是Memoizer的代码:

    public class Memoizer<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 Memoizer(Computable<A, V> c) { this.c = c; }
    
    public V compute(final A arg) throws InterruptedException {
        while (true) {
            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());
            }
        }
    }
    

1 个答案:

答案 0 :(得分:5)

  
      
  1. 提前检查,前面的最后一步肯定会返回cache.putIfAbsent(arg, ft);
  2. 的非空值   

如果只有一个线程正在调用compute,那么cache.putIfAbsent(arg, ft);始终返回null,因为之前没有值。

如果有两个或多个线程同时调用compute方法,那么其中只有一个会从null获得cache.putIfAbsent(arg, ft);,其他线程将获得ft创建了null的帖子。

在这种情况下,其他线程会丢弃他们的FutureTask实例并继续他们从cache.putIfAbsent(arg, ft);

收到的实例
  
      
  1. 第二次检查中的ft.run()没有任何意义,因为此后会立即调用f.get()。
  2.   

您需要run FutureTask以便get稍后run的价值。如果您不致电run,您将永远无法获得价值。创建存储在缓存中的FutureTask的线程将get,然后compute将立即返回,因为此时它已经完成。

但是同时调用putIfAbsent且从get获得非空值的其他线程将转到run调用并等待直到第一个线程完成object-fit方法。