####addWaiter
该方法用于向等待队列中添加一个节点,对于获取独占锁的方法(acquire)传入的mode是EXCLUSIVE,获取共享锁的传入的是SHARED
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//先尝试快速添加到队列中,如果失败则进入enq方法
Node pred = tail;
if (pred != null) {
//这一句值得仔细琢磨,先将node的前驱节点设为tail
node.prev = pred;
//使用cas方法保证多线程情况下的同步
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
这里使用cas保证多线程竞争情况下的同步。有一个值得注意的地方是先将新节点的前驱节点设为旧的tail,然后再将新节点设为tail节点,如果不这么做在执行完compareAndSetTail的一瞬间,新的tail节点与前面的节点实际上是断裂的,而这时候有可能其他线程会使用tail节点往前遍历(unparkSuccessor)
####enq
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
循环,直到节点插入队列尾部,如果尾节点是null,那么初始化队列
####acquireQueued
已经添加到等待队列中的节点获取独占锁,不响应线程中断,acquire方法和条件等待方法会使用该方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
//中断标志
boolean interrupted = false;
for (;;) {
//获取当前节点的前驱节点
final Node p = node.predecessor();
//如果前驱节点是头结点就尝试获取锁, 思考这里为什么以前驱节点是头结点作为判断条件???
if (p == head && tryAcquire(arg)) {
//如果当前线程获取到了锁,那么将该节点设置为头结点
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//如果没有获取到锁,判断是否需要挂起,如果需要则挂起线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
思考这里为什么以前驱节点是头结点作为判断条件???
####shouldParkAfterFailedAcquire
检查并更新获取锁失败的节点的状态,判断等待在该节点上的线程是否可以阻塞。这个方法是在获取锁的循环中最主要的信号控制的地方。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
* 如果前驱节点的状态已经是SIGNAL,那么本节点可以安心地阻塞了,反正前驱节点释放锁时一定会通知的
* 如果前驱节点正常释放锁,会通过unparkSuccessor通知到后继节点,
* 如果前驱节点取消了,在cancelAcquire方法中一定会保证最终能够通知到后继节点,cancelAcquire方法考虑到了各种情形
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
* 跳过所有取消等待的前驱节点
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
* 剩余的情况是0或者PROPAGATE状态,意味着我们需要得到通知,但是暂时不挂起
* 将前驱节点的状态设置为SIGNAL,然后我们再尝试获取一次锁,如果还获取不到那么再挂起
* 思考一下这里如果设置状态失败了会怎样???什么情况下会设置失败???
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
//除了前驱节点的状态已经是SIGNAL的情况,其他情况均不挂起
return false;
}
思考一下compareAndSetWaitStatus(pred, ws, Node.SIGNAL);这一句,如果设置状态失败了会怎样???什么情况下会设置失败???
####parkAndCheckInterrupt
将线程挂起,并检测挂起期间是否被中断过
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
我们看一下挂起的线程在哪些情况下会被唤醒:
/**
* Disables the current thread for thread scheduling purposes unless the
* permit is available.
*
* If the permit is available then it is consumed and the call returns
* immediately; otherwise
* the current thread becomes disabled for thread scheduling
* purposes and lies dormant until one of three things happens:
* 当前线程会陷入阻塞直到一下三种情况中的任意一种发生:
*
*
* - Some other thread invokes {@link #unpark unpark} with the
* current thread as the target; or
* 其他的线程对该线程调用unpark方法
*
*
- Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
* 其他的线程对该线程调用interrupt方法
*
*
- The call spuriously (that is, for no reason) returns.
*
*
* This method does not report which of these caused the
* method to return. Callers should re-check the conditions which caused
* the thread to park in the first place. Callers may also determine,
* for example, the interrupt status of the thread upon return.
*
* @param blocker the synchronization object responsible for this
* thread parking
* @since 1.6
*/
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
后面会看到,如果前面的线程释放锁或者取消等待,那么会通过unpark方法将后继线程唤醒。
####cancelAcquire
最后,如果循环获取锁的过程中发生了异常,就会走到这一步。
思考会发生什么异常???什么情况下会发生异常???
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors
// 跳过所有取消等待的前驱节点
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
//将前驱节点的next应用保存下来,后面cas会用到
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
//如果该节点是tail节点,那么移除该节点就行
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
// 如果后继节点需要通知,那么设置前驱节点的状态为SIGNAL,
// 否则唤醒后继节点
int ws;
// 条件:前驱节点不是头结点(如果前驱节点是头结点,那么就需要唤醒后继节点);
// 前驱节点的状态是SIGNAL或者能够设置为SIGNAL
// 前驱节点的线程不是空,也即有活跃的线程保证能够通知到后继节点
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
//其他情况需要唤醒后继节点
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
####release
释放独占锁。通常用于实现Lock.unlock方法
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
//思考:什么情况下head是null???
// 为什么 waitStatus==0时就不需要唤醒后继节点???
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
思考一个问题:假设A线程正在释放锁,运行到了unparkSuccessor方法的646行之前(还没有将状态设置为0),
这时后继节点的线程B正好运行到了shouldParkAfterFailedAcquire方法的802行(之前已经有一轮没获取到锁,前驱节点的状态已经被设置为SIGNAL),
这时线程A继续运行,将unparkSuccessor方法跑完,
接着线程B接着运行,接下来线程B将会阻塞,但是前面线程A已经完成了锁的释放,已经unpark线程B过了,不会再次unpark,那么这种情况下线程B岂不是再也得不到唤醒??
这个问题的答案就在LockSupport.unpark方法中,我们看一下unpark方法的注释
/**
* Makes available the permit for the given thread, if it
* was not already available. If the thread was blocked on
* {@code park} then it will unblock. Otherwise, its next call
* to {@code park} is guaranteed not to block. This operation
* is not guaranteed to have any effect at all if the given
* thread has not been started.
* 使得线程得到许可运行。
* 如果线程已经被阻塞,那么该方法让线程接触阻塞状态;
* 如果线程没有被阻塞,那么该方法是的下一次对该线程调用park方法不会阻塞,会立即返回
*
* @param thread the thread to unpark, or {@code null}, in which case
* this operation has no effect
*/
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
看到这里是不是瞬间明白了,线程A在死之前已经将后事交代好了,留下了锦囊妙计,所以无论B线程的park在A线程的unpark之前还是之后,B线程都肯定不会阻塞。
####获取共享锁acquireShared
不响应中断
public final void acquireShared(int arg) {
//没有获取到资源量
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
#####doAcquireShared
private void doAcquireShared(int arg) {
//addWaiter方法我们前面已经讲过,就是添加一个等待节点到队列尾部,
//因为是获取共享锁,所以这里传入的是SHARED
final Node node = addWaiter(Node.SHARED);
//标志获取锁失败
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
//如果成功获取到锁,那么向后传播,通知后面等待的节点
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
//阻塞,等待唤醒,循环直到获取锁
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
doAcquireShared方法和acquireQueued方法总的逻辑差不多,都是添加一个节点到队列尾部,然后线程在这个节点上循环直到获取锁,不同是doAcquireShared方法在获取到资源之后会通知后继节点继续获取资源,所以我们看一下setHeadAndPropagate方法
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
//首先设置头结点
setHead(node);
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus either before
* or after setHead) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* 决定是否往后传播的条件:
* propagate即剩余资源量大于零,或者状态<0
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
// 这段代码没看懂,不知道什么情况下h==null,或者(h = head) == null
//
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
// 这里之所以s可能是null可以看addWaiter方法,cas tail节点的一瞬间,前驱节点是没有指向tail节点的引用的
if (s == null || s.isShared())
doReleaseShared();
}
}
#####doReleaseShared
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
* 确保释放锁之后能够传递下去,即是有其他线程正在获取锁或者释放锁。
* 如果头结点的状态是signal,那么唤醒后继节点。如果头结点状态不是signal,那么将状态设为PROPAGATE,以确保一旦线程释放锁,能够正常传播下去
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
//由于当前线程释放了锁,所以必须要将状态设为0,防止后继节点阻塞(见shouldParkAfterFailedAcquire)
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
虽然懂大概的意思,但是深究细节的话还是有很多不明白的地方。暂时先跳过,继续看释放锁的代码
####releaseShared
释放锁的核心代码也是doReleaseShared方法,这个方法还得慢慢啃啊。