Java中的并发缓存

时间:2013-05-10 14:45:35

标签: java caching concurrency concurrenthashmap

我正在寻找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()?

3 个答案:

答案 0 :(得分:1)

putIfAbsent的返回值是现有的一个(如果有的话)或null如果没有,我们将新的一个放入。

        f = cache.putIfAbsent(arg, ft);

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

所以if ( f == null )表示“我们是否将ft放入缓存中了?”。显然,如果我们 将其放入缓存中,我们现在需要将f设置为缓存中的ft,即ft

如果我们没有将f放入缓存中,那么putIfAbsent已经是缓存中的那个,因为它是{{1}}返回的值。

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

代码的想法如下。请求计算某些值来自不同的线程。如果一个线程已启动某些值的计算,则需要相同结果的其他线程不应重复计算,而应等待初始计算。计算完成后,结果将保留在缓存中。

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