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实现, 我有三点疑惑:
他为什么可以不加锁?
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值呢?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
内部实现用了锁,你用它的时候就不需要再加一层锁了
2/3. 写入没完成的时候就读,就会读到null,此时锁是被写操作lock的,等待写操作unlock,再去读就可以读到了。
get
无需加锁,只是读取并不会对数据发生改变readValueUnderLock
正常情况不会被执行e.value
不会为null
作者Doug Lea
解释如下在高版本的jdk中
ConncurrentHashMap
已经完全重写了,这部分代码也没有了。ConncurrentHashMap 是jdk5引入的,它本身的操作就是保证了线程安全的,因此不需要加锁了
经典读写模型,读一般不用加锁的
Get 操作并没有对底层的数组或者链表产生更改操作。楼主的疑问可能是Get的时候另一个线程同时put,或者get的时候另一个线程同时remove。其实这里没问题的 ,如果get在前put在后,那就get到null啦,如果get后remove在前,那也会拿到null啦。不会出现put在前反而get到null的情况或者remove再前反而拿到obj的情况。因为remove或者put最终的操作就是原子的设置 segment[i].table[j]. get操作没有机会对该操作捣乱