Java 在这种情况下,ConcurrentHashMap是否可能出现死锁?

Java 在这种情况下,ConcurrentHashMap是否可能出现死锁?,java,deadlock,java.util.concurrent,concurrenthashmap,Java,Deadlock,Java.util.concurrent,Concurrenthashmap,我正在阅读JDK8中ConcurrentHashMap的源代码,请注意TreeBin使用“读写”锁来防止并发读写 如果没有并发写线程试图修改树结构,则读线程将通过树节点。完成“查找”操作后,读取线程可能: (1) “CAS”表示lockState,如果存在,则“unpk”表示服务员(写入者)线程 下面是源代码中的“find()”方法 最终节点查找(int h,对象k){ 如果(k!=null){ 对于(节点e=first;e!=null;){ int-s;K-ek; 如果((s=lockStat

我正在阅读JDK8中
ConcurrentHashMap
的源代码,请注意
TreeBin
使用“读写”锁来防止并发读写

如果没有并发写线程试图修改树结构,则读线程将通过树节点。完成“查找”操作后,读取线程可能:

(1) “CAS”表示
lockState
,如果存在,则“unpk”表示服务员(写入者)线程

下面是源代码中的“find()”方法

最终节点查找(int h,对象k){
如果(k!=null){
对于(节点e=first;e!=null;){
int-s;K-ek;
如果((s=lockState)和(waterr | WRITER))!=0){
如果(e.hash==h&&
((ek=e.key)=k | |(ek!=null&&k.equals(ek)))
返回e;
e=e.next;
}
否则,如果(美国)与美国相比,
s+读卡器){
树烯醇r,p;
试一试{
p=((r=根)=null?null:
r、 findTreeNode(h,k,null));
}最后{
螺纹w;
//(1)如果没有更多的读者,请尝试取消与服务员的联系(如果有)
if(U.GetAndAdd(此,锁状态,-读卡器)==
(读卡器|服务员)&&(w=服务员)!=null)
锁支持。未解析(w);
}
返回p;
}
}
}
返回null;
}
另一方面,写入线程可能:

  • (2) 使用“CAS”操作将
    waterer
    状态添加到
    lockState

  • (3) 将自身设置为
    变量

  • (4) “公园”本身

以下是作者的代码:

private final void contelledlock(){
布尔等待=假;
对于(int s;;){
if((s=lockState)&~waterer)==0){
如果(美国比较数据(此、锁状态、s、写入程序)){
如果(等待)
服务员=空;
返回;
}
}
如果((服务生和服务员)==0),则为else{
如果(美国比较Wapint(这个,LOCKSTATE,s,s |服务员)){
等待=真;
waterer=Thread.currentThread();
}
}
否则,如果(等待)
锁支持。停车(本);
}
}
以下是我的困惑:

如果上面的四个操作按照(2)(1)(3)(4)的顺序运行,那么操作(1)将不会解析任何内容,因为此时“waterer”为null

然后侍者将永远停在没有人可以修理的地方

后续写入操作将在“驻车”线程持有的内在锁上被全部阻止

这是僵局的机会吗


我真的很困惑。我想也许我在源代码中遗漏了一些东西。如果你熟悉它,就需要你的帮助。

这个问题已经提出一年多了。但这是一个很好的谜题。答案如下:

在(2)(1)(3)之后,在contempedlock()中继续执行,如下所示:

if(((s=lockState)&~water)==0)
true,因为(1)已执行

if(U.compareAndSwapInt(this,LOCKSTATE,s,WRITER))
也是true,因为(3)是在
(s=LOCKSTATE)
之前而不是之后执行的

由于在执行(3)之前,
waiting
被设置为true,因此第三个if语句也是true。因此,
waterer
被设置为null,我们退出循环。(4) 永远不会被执行

总而言之:
在(2)(1)(3)之后,将永远不会执行操作(4)。因此,不会出现死锁,我们都可以毫无疑问地继续使用ConcurrentHashMap;-)

另外,请有人在
find
中给我解释一下这句话:
如果((s=lockState)和(watter | WRITER))!=0
?为什么这里和它的块中没有同步?@dyukha的意思是,如果这里有服务员或作家,只需通过“下一步”引用(如链接列表)遍历树即可。因为每次将节点添加到链接列表中时,实际上都会将其添加到“第一个”位置,这不会干扰读取线程遍历列表中的其他节点。所以这里不需要锁