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

    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值呢?

阅读 14.5k
5 个回答
  1. 内部实现用了锁,你用它的时候就不需要再加一层锁了

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

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

  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已经完全重写了,这部分代码也没有了。

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

新手上路,请多包涵

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

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题