1、框架
其类结构扩展点为
有两种资料共享方式:Exclusive和Share
结点状态5种
CANCELED(1):表示结点已取消调度
SIGNAL(-1):表示后继结点在等待当前结点唤醒,后继结点入队时,会将前继结点状态更新为SIGNAL
CONDITION(-2):表示结点等待在Condition上,当其它线程调用Condition的signal方法后,CONDITION状态的结点将从等待队列转移到同步队列中。等待获取同步锁
PROPAGATE(-3):在共享模式下,前继结点不仅会唤醒其后继结点,同时也可能会唤醒后继的后继结点。
0:新结点入队进的默认状态
2、加锁流程
2.1 acquire(int)
独占模式下线程获取共享资料,如果获取到资源,直接返回,否则进入等待队列,直到获取到资料为止,同时会忽略中断标志
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
(1)tryAcquire尝试直接获取资源,如果成功直接返回
(2)addWaiter将该线程加入到队列尾部,并标记为独占模式
(3)acquireQueued使线程阻塞在等待队列中获取资料,一直获取到资料后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false
(4)如果线程在等待过程中被中断过,它是不忽略的。因为在获取到资源后用selfInterrupt清除了中断标志
2.1.1 addWaiter(Node)
将当前线程放入等待队列中尾部,并返回当前线程所在的结点
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
2.1.2 acquireQueued(Node, int)
获取资源失败后,进入阻塞状态等待被唤醒
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;//标记是否成功拿到资源
try {
boolean interrupted = false;//标记等待过程中是否被中断过
//又是一个“自旋”!
for (;;) {
final Node p = node.predecessor();//拿到前驱
//如果前驱是head,即该结点已成老二,那么便有资格去尝试获取资源(可能是老大释放完资源唤醒自己的,当然也可能被interrupt了)。
if (p == head && tryAcquire(arg)) {
setHead(node);//拿到资源后,将head指向该结点。所以head所指的标杆结点,就是当前获取到资源的那个结点或null。
p.next = null; // setHead中node.prev已置为null,此处再将head.next置为null,就是为了方便GC回收以前的head结点。也就意味着之前拿完资源的结点出队了!
failed = false; // 成功获取资源
return interrupted;//返回等待过程中是否被中断过
}
//如果自己可以休息了,就通过park()进入waiting状态,直到被unpark()。如果不可中断的情况下被中断了,那么会从park()中醒过来,发现拿不到资源,从而继续进入park()等待。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;//如果等待过程中被中断过,哪怕只有那么一次,就将interrupted标记为true
}
} finally {
if (failed) // 如果等待过程中没有成功获取资源(如timeout,或者可中断的情况下被中断了),那么取消结点在队列中的等待。
cancelAcquire(node);
}
}
2.1.3 shouldParkAfterFailedAcquire(Node, Node)
主要检查状态是否可以进入waiting状态,如果前驱结点状态是SIGNAL,说明可以,如果是CANCELED,说明前驱结点已经被取消,需要继续向前找直到找到可用状态的结点,将当前结点放入其后。否则将前驱结点状态更新为SIGNAL
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.
*/
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.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
2.1.4 parkAndCheckInterrupt
如果线程找好安全休息点后,就可以安心去休息了。此方法是让线程去休息,真正进入等待状态
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
3、解锁流程
3.1 unparkSuccessor(Node)
该方法是唤醒等待队列中下一个线程
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}