1

我正在从 Brian Goetz 的并发书中寻找对以下代码的解释。

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());
        }
    }
}

此外,在 putIfAbsent() 调用之后,为什么该语句f = ft;而不是直接执行 ft.run() ?

4

3 回答 3

1

的返回值putIfAbsent是现有的,如果已经存在或者null没有,我们将新的放入。

        f = cache.putIfAbsent(arg, ft);

        if (f == null) {
            f = ft;
            ft.run();
        }

所以if ( f == null )意思是“我们放入ft缓存了吗?”。显然,如果我们确实将其放入缓存中,我们现在需要将其设置f为缓存中的那个,即ft.

如果我们没有放入ft缓存,那么f它已经是缓存中的那个了,因为它是返回的值putIfAbsent

于 2013-05-10T15:28:46.000 回答
0

因为您正在返回 f.get() 并可能从缓存中删除 f 。这允许一段代码适用于所有实例。

    try {
        return f.get();
    } catch (CancellationException e) {
        cache.remove(arg, f);

如果您没有将 f 替换为上面对 ft 的引用,那么每次 putIfAbsent 返回 null 时都会得到 NPE。

于 2013-05-10T14:49:49.483 回答
0

代码的思路如下。计算某些值的请求来自不同的线程。如果一个线程发起了某个值的计算,其他需要相同结果的线程不应该重复计算,而是应该等待初始计算。计算完成后,结果保存在缓存中。

这种模式的示例是加载 java 类。如果一个类正在被加载,而另一个线程也请求加载同一个类,它不应该自己加载它,而是等待第一个线程的结果,这样总是不超过一个给定类的实例,加载由同一个类加载器..

于 2013-05-10T16:47:24.267 回答