Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java ConcurrentHashMap.get()如何防止脏读?_Java_Concurrency_Concurrenthashmap - Fatal编程技术网

Java ConcurrentHashMap.get()如何防止脏读?

Java ConcurrentHashMap.get()如何防止脏读?,java,concurrency,concurrenthashmap,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 =

我正在查看
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;
    }
public V get(对象键){
节点[]选项卡;节点e,p;int n,eh;K ek;
int h=spread(key.hashCode());
如果((tab=table)!=null&(n=tab.length)>0&&
(e=tabAt(tab,(n-1)&h))!=null){
如果((eh=e.hash)==h){
if((ek=e.key)==key | |(ek!=null&&key.equals(ek))//在此处标记可能的脏读
返回e.val;
}
else if(eh<0)
返回(p=e.find(h,key))!=null?p.val:null;
while((e=e.next)!=null){
如果(e.hash==h&&
((ek=e.key)==key | |(ek!=null&&key.equals(ek))//在此处标记可能的脏读
返回e.val;
}
}
返回null;
}
我标记的两行正在做相同的事情:检查当前
节点的
键是否等于所需的
键。如果
true
,将返回其相应的值。但是,如果在
return
remove()
将此节点从数据结构中删除之前插入另一个线程,该怎么办呢。由于局部变量
e
仍保留已删除节点的引用,因此GC将保留该引用,而
get()
方法仍将返回已删除的值,从而导致脏读

我错过什么了吗

检索操作(包括
get
)通常不会阻塞,因此可能与更新操作(包括
put
remove
)重叠。检索反映了最近完成的更新操作在开始时的结果。(更正式地说,给定键的更新操作与报告更新值的该键的任何(非null)检索具有“发生在前”关系。)

这通常不是问题,因为如果
get
方法获取了锁,阻止了另一个线程中的更新操作,那么
get
将永远不会返回不可能发生的结果。您得到的结果就好像
get
调用发生在更新操作开始之前一样


因此,如果您不介意
get
是在更新之前还是之后发生,那么您也不应该介意它在更新期间发生,因为在更新期间和更新之前没有明显的差异。如果您确实希望更新后出现
get
,则需要从更新线程发出更新完成的信号;等待获取锁无论如何也无法实现这一点,因为您可能会在更新发生之前获得锁(在这种情况下,您将获得与未获取锁相同的结果)。

谢谢,我想我明白您的意思了。经过多次校对,我没有发现任何漏洞,其他线程造成严重的不一致性,因为一切都是缓存或引用!最坏的情况可能是
返回一个已删除的
,或者对新添加的
抛出一个
NullPointerException
。但是这两种情况可以看作是
get
方法发生在最近的
remove
put
操作之前,非常聪明。这种处理并发读取操作的技术是惊人的!你认为在我们自己的编码工作中应用它是可行的吗?在获取一个不存在(或当前正在添加)的密钥的情况下,它返回null,不应该抛出NPE。原则上,您可以在自己的代码中使用这种推理,但我建议您不要编写自己的并发原语-在更高的抽象级别上工作更安全,使用能够提供所需保证的类。@Zizy,通用并发数据结构的一个重要特性是线性化能力。在
ek
中缓存条目的键有效地为get操作创建了一个线性化点。@kaya3,啊,是的,我在那里犯了一个错误,它应该返回
null
。谢谢你的帮助。