在Brian Goetz的Java Concurrency In Practice中,为什么Memoizer类没有用@ThreadSafe注释?

时间:2010-12-24 21:19:26

标签: java concurrency thread-safety

实践中的Java并发实践Brian Goetz提供了一个用于并发使用的高效可伸缩缓存的示例。显示类Memoizer(第108页)实现的示例的最终版本显示了这样的缓存。我想知道为什么这个类没有用@ThreadSafe注释?缓存的客户端类Factorizer已使用@ThreadSafe正确注释。附录指出,如果一个类没有使用@ThreadSafe或@Immutable注释,则应该假定它不是线程安全的。 Memoizer似乎是线程安全的。

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

}

2 个答案:

答案 0 :(得分:1)

它看起来确实是线程安全的。如果我没记错的话,在这个特定版本的Memoizer中,Goetz展示了使用cache.putIfAbsent来避免缓存的get和put方法之间的竞争条件。

正如其他人所指出的那样,它可能已被排除在外,您将不得不依赖书中的评论来判断这是否是“好”版本。

答案 1 :(得分:0)

问Goetz我猜。这可能只是一种疏忽。

然而,这些注释的问题是它们没有意义。理想情况下,它们需要与FindBugs等自动化错误查找器一起使用,在这种情况下,注释@Immutable(例如)允许FindBugs打开其检测器进行字段突变。

如果没有这样的检查,图书馆的作者很容易将@ThreadSafe@Immutable附加到课堂上,甚至不是真的!我并不是建议故意欺骗,而是诚实的错误......然而结果是图书馆的消费者不能依赖于注释,那么重点是什么?