当两个线程同时执行cache.putIfAbsent时会发生什么?

时间:2013-11-24 06:43:03

标签: java multithreading concurrency concurrenthashmap futuretask

我正在学习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操作是自动的,并且如果两个线程都可以输入run方法,它们都返回null? / p>

2 个答案:

答案 0 :(得分:1)

putIfAbsent保证线程安全,不仅在于它不会破坏您的数据,还在于始终在最新的副本上运行数据。

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

答案 1 :(得分:1)

所有原子实现的来源都是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是一个原子函数,因此保证线程在执行时不会相互干扰。