Ziz*_*izy 3 java concurrency concurrenthashmap
我正在查看源代码ConcurrentHashMap,想知道该get()方法在没有任何监视器的情况下如何工作,代码如下:
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek))) // mark here for possible dirty read
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek)))) // mark here for possible dirty read
return e.val;
}
}
return null;
}
Run Code Online (Sandbox Code Playgroud)
我标记的两行正在做同样的事情:检查key当前的是否Node<K, V>等于所需的key。如果true,将返回其对应的值。但是如果另一个线程从数据结构中切入之前return和这个节点怎么办?remove()由于局部变量e仍然保存着被删除节点的引用,GC 将保留它,并且该get()方法仍然会返回被删除的值,从而导致脏读。
我错过了什么?
它没有:
检索操作(包括
get)通常不会阻塞,因此可能与更新操作(包括put和remove)重叠。检索反映了最近完成的更新操作在其开始时的结果。(更正式地说,给定键的更新操作与报告更新值的该键的任何(非空)检索具有发生之前关系。)
这通常不是问题,因为永远不会返回如果该方法获取锁就get不可能发生的结果,从而阻塞其他线程中的更新操作。get您只需得到结果,就好像get调用发生在更新操作开始之前一样。
因此,如果您不介意它是get在更新之前还是之后发生,那么您也不应该介意它在更新期间发生,因为在期间和之前之间没有明显的差异。如果您确实希望在更新后get出现这种情况,那么您需要从更新线程发出更新已完成的信号;无论如何,等待获取锁都无法实现这一点,因为您可能会在更新发生之前获得锁(在这种情况下,您将获得与未获取锁相同的结果)。