java并发实践5.19

java并发实践5.19,java,concurrency,Java,Concurrency,在JCIP手册中,清单5.19是存储器的最终实现。我的问题是: 由于原子putIfAbsent(),这里出现了无尽的while循环 while循环是否应该位于putIfAbsent()的impl内部,而不是客户端代码 while循环是否应该在较小的范围内,仅包装putIfAbsent() 而循环的可读性看起来很差 代码: 公共类存储器实现可计算{ 私有最终ConcurrentMap缓存 =新的ConcurrentHashMap(); 私有最终可计算c; 公共存储器(可计算c){this.c=c;

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

  • 由于原子putIfAbsent(),这里出现了无尽的while循环
  • while循环是否应该位于putIfAbsent()的impl内部,而不是客户端代码
  • while循环是否应该在较小的范围内,仅包装putIfAbsent()
  • 而循环的可读性看起来很差
  • 代码:

    公共类存储器实现可计算{
    私有最终ConcurrentMap缓存
    =新的ConcurrentHashMap();
    私有最终可计算c;
    公共存储器(可计算c){this.c=c;}
    public V compute(最后一个参数)抛出InterruptedException{
    
    while(true){/No,您不能缩小while循环的范围。您需要对缓存中的值执行
    f.get()
    。如果映射中没有
    arg
    的值,您需要对结果执行
    get()
    ,否则您需要获取映射中
    arg
    的现有值并
    get()
    那个

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

    似乎您可以尝试将更多的
    f.get()
    从循环中删除,但由于
    CancellationException
    的风险,您需要继续尝试

    1) 由于原子putIfAbsent(),这里出现了无尽的while循环

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

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

    不,请阅读
    putIfAbsent
    的功能。它只尝试放置一个对象一次

    3) while循环是否应该在较小的范围内,仅包装putIfAbsent()

    不,不应该。看

    4) 而循环的可读性看起来很差


    你可以自由地提供更好的服务。事实上,这种结构非常适合你必须尝试做某事直到成功为止的情况。

    谢谢。我认为while循环只适用于取消异常,而不适用于执行异常,因为可洗可弃,只需将ExecutionException重新设置到范围之外。@ying Yep,请注意g异常名称。为什么复制和粘贴代码是个坏主意。除了这里回答的while问题之外,我想知道为什么需要以下代码:if(f==null){f=ft;ft.run();}原因:1.前一个代码不应该返回null 2。还有一个if(f==null)已经存在ahead@DenisWang
    if(f==null){f=ft;ft.run();}
    代码之所以存在,是因为前一行,
    f=cache.putIfAbsent(arg,ft);
    如果键不存在(或映射到null),则返回null.
    f
    应该是规范缓存值,因此如果在检查之前它是
    null
    ,我们知道
    ft
    作为规范值进入映射。然后我们执行它并将其分配给
    f
    返回。你是对的。如果Q1为真,我的问题2,3,4是后续问题。当然,Q1不是真的,所以所有后续问题都是are无效。除了这里回答的while问题,我想知道为什么需要以下代码:if(f==null){f=ft;ft.run();}原因:1.前一个代码不应该返回null 2。还有另一个if(f==null)除了这里回答的while问题之外,我想知道为什么需要以下代码:if(f==null){f=ft;ft.run();}原因:1.前一个代码不应该返回null 2。前面已经存在另一个if(f==null)
    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());
            }
         }
       }
    }