可以看到该方法也是使用for(;;)无限循环的方式来尝试获取锁。首先判断当前节点是否为头结点的下一个节点,如果是则再次调用tryAcquire尝试获取锁。当然这个过程并不是一定不停进行的,这样的话多线程竞争下cpu切换也极耗费资源。
shouldParkAfterFailedAcquire会判断是否对当前节点进行阻塞,阻塞之后只有当unpark后节点才会继续假如争夺锁的行列。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
判断一个节点是否需要被阻塞是通过该节点的前继节点的状态判断的。
如果前继节点状态为singal,则表示前继节点还在等待,当前节点需要继续被阻塞。返回true。
如果前继节点大于0,则表示前继节点为取消状态。取消状态的节点不参与锁的竞争,直接跳过。返回false。
如果前继节点时其他状态(0,PROPAGATE),不进行阻塞,表示当前节点需要重试尝试获取锁。返回false。
shouldParkAfterFailedAcquire方法如果返回true,表示需要将当前节点阻塞,阻塞方法为parkAndCheckInterrupt。
AbstractQueuedSynchronizer#parkAndCheckInterrupt
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
阻塞是通过LockSupport进行阻塞,被阻塞的节点不参与锁的竞争(不在进行循环获取锁),只能被unpark后才继续竞争锁。
而被阻塞的节点要被释放则依赖于unlock方法。
unlock
ReentrantLock 中unlock的实现是通过调用AQS的AbstractQueuedSynchronizer#release方法实现。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
release调用tryRelease方法,tryRelease是在ReentrantLock中实现。
ReentrantLock#tryRelease
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
tryRelease方法逻辑很简单,首先减去releases(一般为1)表示释放一个锁,如果释放后state=0表示释放锁成功,后续等待的节点可以获取该锁了。如果state!=0则表示该锁为重入锁,需要多次释放。
当释放锁成功后(state=0),会对头结点的后继节点进行unpark。