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实现, 我有三点疑惑:
他为什么可以不加锁?
readValueUnderLock有什么用呢?
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值呢?
内部实现用了锁,你用它的时候就不需要再加一层锁了
2/3. 写入没完成的时候就读,就会读到null,此时锁是被写操作lock的,等待写操作unlock,再去读就可以读到了。