gpt4 book ai didi

java - 为什么 `h`这个参数在AbstractQueuedSynchronizer中会判断两次是否为null?

转载 作者:行者123 更新时间:2023-12-01 14:31:48 25 4
gpt4 key购买 nike

最近我在学习并发。当我对Semaphore了解更多时,我有一些问题。

这是JDK1.8中AbstractQueuedSynchronizer的代码(727行):

private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);

if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}

这里为什么要判断h两次是否为null? h 什么时候可以为空?我认为它们都不能为空。

最佳答案

因为头节点是动态移动的,调用 doReleaseShared() 来自两个方面:

1.持有锁的线程调用release()然后执行doReleaseShared()

2.someone Thread执行acquire(),preNode为head获取锁成功获取锁后执行doReleaseShared();

考虑以下可能的执行顺序:

这是一些节点:head--->init node--->node1--->node2

有人释放锁然后唤醒node1,node1从unpark中恢复,(node1的Thread named Thread1,node2的Thread named Thread2 ...)node1的preNode为head,获取成功,此时permit为1。

private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);//success , r=0;
if (r >= 0) { //true
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}

而 Thread1 在执行 setHead(node) 后暂时挂起。

private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Thread2 come here and the Thread1 execute continue
setHead(node);

//Note: this point,the setHead(node) is done,but time slice
// exhaustion,Thread1 is temporarily suspended


if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}

另一个Thread释放锁,当前head为node1,CAS设置node1的waitStatus==0,唤醒node2。

node2 preHead为node1,获取锁成功,然后执行setHeadAndPropagate()方法。

碰巧节点node = head,Thread2继续...

if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
//return immediate ,because
//to old head
//propagate=0 false
//h==null fase
//h.waitStatus=0 false
//to new head node1
//propagate=0 false
//h==null fase
//h.waitStatus=0 false

我们回去吧

 private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC //node1 could be GC!!!
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}

最后,我们回到Thread2,Thread2继续。

当node2在setHeadAndPropagate()方法第990行判断if(...)

h==null 可能会发生。所以 doReleaseShared()

总结:根据你的要求,第一个h表示旧的head,它可以是null(特殊场景,时间分片引起的线程调度),但是后面的(h=head==null) 不能为null,因为它是当前的Thread2,表示当前还活着。你可以把它当作例行检查,不要介意。

关于java - 为什么 `h`这个参数在AbstractQueuedSynchronizer中会判断两次是否为null?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62770269/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com