实践中的java并发5.19

时间:2012-12-20 22:29:34

标签: java concurrency

在JCIP一书中,清单5.19存储器的最终实现。我的问题是:

  1. 由于原子putIfAbsent()?
  2. ,无休止的while循环在这里
  3. 应该将while循环放在putIfAbsent()的impl中而不是客户端代码吗?
  4. 如果while循环在较小的范围内,只需包装putIfAbsent()?
  5. while循环在可读性上看起来很糟糕
  6. 代码:

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

2 个答案:

答案 0 :(得分:3)

  

1)由于原子putIfAbsent()?

,无限循环在这里

此处的while循环用于在取消计算时重复计算(try中的第一种情况)。

  

2)while循环是否应该在putIfAbsent()的impl内部而不是客户端代码?

不,请阅读putIfAbsent的内容。它只是尝试将对象放置一次。

  

3)while循环是否应该包含putIfAbsent()?

不,不应该。见#1。

  

4)虽然循环在可读性方面看起来很糟糕。

你可以自由地提供更好的东西。事实上,这种建筑套件非常适合在必须尝试做某事直到成功进行的情况。

答案 1 :(得分:2)

不,你不能减少while循环的范围。您希望对缓存中的值执行f.get()。如果地图中的arg没有值,您希望对结果执行get(),否则您希望获取地图中arg的现有值get()那一个。

问题是此实现中没有锁,因此在检查是否存在值并尝试插入值之间,另一个线程可能已插入其自己的值。同样,可能是插入失败和检索之间的情况,该值可能已从缓存中删除(由于CancellationException)。由于这些失败案例,您可以在while(true)中旋转,直到您可以从地图中获取规范值,或者在地图中插入新值(使您的值成为规范)。

您似乎可以尝试更多地将f.get()移出循环,但由于CancellationException的风险,您需要继续尝试。