concurrenthashmap - 看java并发编程实践对ConcurrentHash使用方法的疑问

 张心虚要加油 发布于 2022-11-07 23:36

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

private final ConcurrentHashMap> cache = new ConcurrentHashMap>();

private final Computable c;

public Memoizer(Computable 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 f = cache.get(arg);
        if (f == null) {
            // 
            Callable eval = new Callable() {
                @Override
                public V call() throws Exception {
                    return c.compute(arg);
                }
            };
            FutureTask ft = new FutureTask(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 个回答
  • 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的情况。

    2022-11-12 01:40 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有