Beacause the head node is dynamic move ,
invoke doReleaseShared() come from two sides:
1.held the lock's Thread invoke release()then execute doReleaseShared().
2.someone Thread execute acquire() ,preNode is head and acquire lock success the lock and then execute doReleaseShared();
Considering the following possible order of execution:
here is some node :
head--->init node--->node1--->node2
someone release lock and then wake up the node1,the node1 resumed from unpark,(node1's Thread named Thread1,node2's Thread named Thread2...)
node1's preNode is head,and acquire success,permit is one at the point.
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);
}
}
while the Thread1 is temporarily suspended after execute 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();
}
}
another Thread release lock.current head is node1,CAS set node1's waitStatus==0,and wake up node2 .
node2 preHead is node1 and acquire lock success.then execute setHeadAndPropagate() method .
It happened that Node node = head,and the Thread2 continue...
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
so let's back
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);
}
}
finally,we back to Thread2,Thread2 continue.
when node2 judge the if(...) in setHeadAndPropagate() method line 990.
the h==null maybe occur. so doReleaseShared()
Summary:
as your requestion ,the first h means the old head ,it could be null(Special scenario, thread scheduling caused by time slicing),but the subsequently (h=head==null) must not be null,beacause it's the current Thread2,means the currently alive.you can think of it as a routine examination,don't mind.