concurrenthashmap - 看java并发编程实践对ConcurrentHash使用方法的疑问
黄舟
黄舟 2017-04-17 11:15:42
[Java讨论组]

根据书里面的实现高效本次缓存的代码,下面的代码的前提是,假设有一个很耗时的计算,并且计算结果可以重用,我想将这个计算结果缓存在map里,并且保证计算过程(代码中的Callable代码)只会被执行一次。

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);
            // **这里**
            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);
        }
    }
}

public static RuntimeException launderThrowable(Throwable t) {
    if (t instanceof RuntimeException)
        return (RuntimeException) t;
    else if (t instanceof Error)
        throw (Error) t;
    else
        throw new IllegalStateException("Not unchecked", t);
}

我的分析是,putIfAbsent既然只能保证原子性,如果两个线程同时执行这个方法,那么会同时返回null,继而同时进入下面的if代码块,最后还是会导致compute执行了两次。
如果分析错误,那么正确的理解应该是怎样的呢?

黄舟
黄舟

人生最曼妙的风景,竟是内心的淡定与从容!

全部回复(1)
PHPz

ConcurrentHashMap的源码找到的原因:

public V putIfAbsent(K key, V value) {
    if (value == null)
        throw new NullPointerException();
    int hash = hash(key.hashCode());
    return segmentFor(hash).put(key, hash, value, true);
}

SegmentFor的put方法有加锁操作:

 V put(K key, int hash, V value, boolean onlyIfAbsent) {
    lock();
    try {
        // ...
    } finally {
        unlock();
    }
}

这样就保证了不会有两个线程同时返回null的情况。

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号