【问题标题】:What will happen when two threads execute cache.putIfAbsent at the same time?两个线程同时执行 cache.putIfAbsent 会发生什么?
【发布时间】:2013-11-24 06:43:02
【问题描述】:

我正在学习Java Concurrency in Practice,但是有些代码让我很困惑:

private final ConcurrentHashMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();

private final Computable<A, V> c;

public Memoizer(Computable<A, V> c) {
    this.c = c;
}

/* (non-Javadoc)
 * @see com.demo.buildingblocks.Computable#compute(java.lang.Object)
 */
@Override
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>() {
                @Override
                public V call() throws Exception {
                    return c.compute(arg);
                }
            };
            FutureTask<V> ft = new FutureTask<V>(eval);
            // what will happen when two threads arrive here at the same time?
            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) {
            launderThrowable(e);
        }
    }
}

我就是不明白,既然putIfAbsent只能保证put操作是原子的,而且都返回null,如果两个线程都可以进入run方法呢?

【问题讨论】:

    标签: java multithreading concurrency concurrenthashmap futuretask


    【解决方案1】:

    putIfAbsent 保证线程安全不仅在于它不会破坏您的数据,而且在于它始终处理数据的最新副本.

    此外,如果存在这样的值,它不会返回地图中的前一个值。所以第一次调用putIfAbsent 会成功,并返回null,因为没有先前的值。第二个调用将阻塞,直到第一个成功,然后返回放入映射中的第一个值,导致第二个 run() 永远不会被调用。

    【讨论】:

    • 我的意思是,两个调用同时到达,并且都调用了putIfAbsend方法,然后返回null,如果这种感觉可以发生?
    • 不,这不可能发生。 一个调用总是首先发生,第二个将等待它完成。这是ConcurrentMap 的胎面安全的一部分。
    • 看了ConcurrentMap的源码,在put操作中找到了lock,所以你说的对,不会出现这种情况,谢谢!
    【解决方案2】:

    所有原子实现的来源是compareAndSet(expected, newValue) 方法。因此,如果 2 个线程到达并且每个线程都喜欢设置一个位于 - 比如说 - 1 到 3 的元素,则会发生以下情况:

    Thread A: value.compareAndSet(1, 3) - success: value is now 3, return true.
    Thread B: value.compareAndSet(1, 3) - error: value is not 1 as expected, return false.
    

    未定义哪些线程先到达,但由于 compareAndSet 是原子函数,因此可以保证线程在执行时不会相互干扰。

    【讨论】:

      猜你喜欢
      • 2014-08-31
      • 2019-11-03
      • 1970-01-01
      • 2014-03-18
      • 1970-01-01
      • 1970-01-01
      • 2019-11-05
      • 2017-11-18
      • 2019-05-16
      相关资源
      最近更新 更多