加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 使用常规HashMap双重检查锁定

发布时间:2020-05-31 07:44:22 所属栏目:Java 来源:互联网
导读:回到并发.到目前为止,很明显,对于双重检查锁定,变量需要声明为volatile.但是,如果使用双重检查锁定,如下所示. class TestA, B { private final MapA, B map = new HashMap(); public B fetch(A key, FunctionA, B loader) { B value =

回到并发.到目前为止,很明显,对于双重检查锁定,变量需要声明为volatile.但是,如果使用双重检查锁定,如下所示.

class Test<A,B> {

    private final Map<A,B> map = new HashMap<>();

    public B fetch(A key,Function<A,B> loader) {
        B value = map.get(key);
        if (value == null) {
            synchronized (this) {
                value = map.get(key);
                if (value == null) {
                    value = loader.apply(key);
                    map.put(key,value);
                }
            }
        }
        return value;
    }

}

为什么它必须是ConcurrentHashMap而不是常规的HashMap?所有映射修改都在synchronized块中完成,代码不使用迭代器,因此从技术上讲,应该没有“并发修改”问题.

请不要建议使用putIfAbsent / computeIfAbsent,因为我在询问概念而不是使用API :)除非使用此API有助于HashMap与ConcurrentHashMap主题.

更新2016-12-30

Holger的评论回答了这个问题“HashMap.get没有修改结构,但你的put调用确实如此.因为在调用synchronized块之外调用get,它可以看到put的不完整状态行动同时发生.“谢谢!

解决方法

这个问题混淆了很多,很难回答.

如果只从一个线程调用此代码,那么你就太复杂了;你不需要任何同步.但显然这不是你的意图.

因此,多个线程将调用fetch方法,该方法在没有任何同步的情况下委托给HashMap.get(). HashMap不是线程安全的.巴姆,故事的结尾.如果您正在尝试模拟双重检查锁定,则无关紧要;实际情况是在地图上调用get()和put()将操纵HashMap的内部可变数据结构,而不会在所有代码路径上进行一致同步,并且因为您可以从多个线程同时调用这些,所以您已经死.

(另外,您可能认为HashMap.get()是一个纯读操作,但这也是错误的.如果HashMap实际上是一个LinkedHashMap(它是HashMap的子类)怎么办.BlinkHashMap.get()会更新访问顺序这涉及到内部数据结构的写入 – 这里同时没有同步.但是即使get()没有写入,你的代码仍然会被破坏.)

经验法则:当你认为你有一个巧妙的技巧可以让你避免同步时,你几乎肯定是错的.

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读