最近我在学习并发。当我对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/