ConcurrentHashMap的get为什么可以不加锁?

发布于 2022-09-03 23:43:42 字数 1368 浏览 26 评论 0

    V get(Object key, int hash) {
        if (count != 0) { // read-volatile
            HashEntry<K,V> e = getFirst(hash);
            while (e != null) {
                if (e.hash == hash && key.equals(e.key)) {
                    V v = e.value;
                    if (v != null)
                        return v;
                    return readValueUnderLock(e); // recheck
                }
                e = e.next;
            }
        }
        return null;
    }
    
    V readValueUnderLock(HashEntry<K,V> e) {
        lock();
        try {
            return e.value;
        } finally {
            unlock();
        }
    }
    

这是jdk1.6的ConcurrentHashMap的Segement的get实现, 我有三点疑惑:

  1. 他为什么可以不加锁?

  2. readValueUnderLock有什么用呢?

  3. e.value为什么有可能为null呢?

对于第一个问题, 书上的解释是: 因为count和e.value都是volatile的, 所以读写具有原子性,并且根据happen-before原则, 读,写同时发生时, 写先于读发生. 但是我读了put方法和remove方法, 他都是复制出一个新的数组进行复制, 而非在原地操作, 因此完全有可能出现remove方法在remove的同时, get方法在获取, 导致get到一个被删除的值.该怎么理解这个问题?

对于第三个问题:

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

ConcurrentHashMap会对放入了value进行检查, 为什么会出现null值呢?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

你怎么这么可爱啊 2022-09-10 23:43:42
  1. 内部实现用了锁,你用它的时候就不需要再加一层锁了

        『他都是复制出一个新的数组进行复制, 而非在原地操作』 你在哪里看到有复制的代码?

    2/3. 写入没完成的时候就读,就会读到null,此时锁是被写操作lock的,等待写操作unlock,再去读就可以读到了。

↘人皮目录ツ 2022-09-10 23:43:42
  1. get 无需加锁,只是读取并不会对数据发生改变

  2. readValueUnderLock 正常情况不会被执行

  3. e.value 不会为 null 作者Doug Lea解释如下

Not quite. You are right that it should never be called. However, the JLS/JMM can be read as not absolutely forbidding it from being called because of weaknesses in required ordering relationships among finals vs volatiles set in constructors (key is final, value is volatile), wrt the reads by threads using the entry objects. (In JMM-ese, ordering constraints for finals fall outside of the synchronizes-with relation.) That's the issue the doc comment (pasted below) refers to. No one has ever thought of any practical loophole that a processor/compiler might find to produce a null value read, and it may be provable that none exist (and perhaps someday a JLS/JMM revision will fill in gaps to clarify this), but Bill Pugh once suggested we put this in anyway just for the sake of being conservatively pedantically correct. In retrospect, I'm not so sure this was a good idea, since it leads people to come up with exotic theories.

在高版本的jdk中ConncurrentHashMap已经完全重写了,这部分代码也没有了。

懒猫 2022-09-10 23:43:42

ConncurrentHashMap 是jdk5引入的,它本身的操作就是保证了线程安全的,因此不需要加锁了

糖粟与秋泊 2022-09-10 23:43:42

经典读写模型,读一般不用加锁的

天荒地未老 2022-09-10 23:43:42

Get 操作并没有对底层的数组或者链表产生更改操作。楼主的疑问可能是Get的时候另一个线程同时put,或者get的时候另一个线程同时remove。其实这里没问题的 ,如果get在前put在后,那就get到null啦,如果get后remove在前,那也会拿到null啦。不会出现put在前反而get到null的情况或者remove再前反而拿到obj的情况。因为remove或者put最终的操作就是原子的设置 segment[i].table[j]. get操作没有机会对该操作捣乱

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文