AQS 继承AOS(含独占线程) volatile int state 组合使用
可重写tryAcquire tryRelease tryAcquireShared tryReleaseShared isHeldExclusively
内含Node: 双向链表(同步队列),thread,waitStatus,nextWaiter(构成条件等待队列,用于Condition.signal/signalAll)
waitStatus:1(取消,由于等待超时或中断),大于0无效
0 初始化
SIGNAL 常见,会unpark下一个有效Node thread ,(因此一定要排在signal这样的node后面,安全点)
CONDITION 当前线程在条件队列上等待
PROPOGATE
**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;//返回等待过程中是否被中断过
}
//如果自己可以休息了,就进入waiting状态,直到被unpark()
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;//如果等待过程中被中断过,哪怕只有那么一次,就将interrupted标记为true
}
} finally {
if (failed)
cancelAcquire(node);
}
}**
**private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;//拿到前驱的状态
if (ws == Node.SIGNAL)
//如果已经告诉前驱拿完号后通知自己一下,那就可以安心休息了
return true;
if (ws > 0) {
/*
* 如果前驱放弃了,那就一直往前找,直到找到最近一个正常等待的状态,并排在它的后边。
* 注意:那些放弃的结点,由于被自己“加塞”到它们前边,它们相当于形成一个无引用链,稍后就会被保安大叔赶走了(GC回收)!
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//如果前驱正常,那就把前驱的状态设置成SIGNAL,告诉它拿完号后通知自己一下。有可能失败,人家说不定刚刚释放完呢!
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}**
private void unparkSuccessor(Node node) {
//这里,node一般为当前线程所在的结点。
int ws = node.waitStatus;
if (ws < 0)//置零当前线程所在的结点状态,允许失败。
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;//找到下一个需要唤醒的结点s
if (s == null || s.waitStatus > 0) {//如果为空或已取消
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)//从这里可以看出,<=0的结点,都是还有效的结点。
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);//唤醒
}
说明:最开始无head和tail,第一个thread进来排队head为空节点,自己排在其后面,每一次都唤醒第二节点(也有特殊情况),第二节点醒来且获取到号自己做head
特殊情况:
1 老二节点醒来获取不到锁:在唤醒醒来之间被抢占了,继续睡
2 中断超时醒来,自己状态置为无效,退出
3 第二节点已经是无效的,那么唤醒第一个有效节点,这会导致非老二节点被唤醒,醒来后找安全点自己就是老二了
安全点:即排signal后面,前面是有效但非signal-改成signal,前面是无效(>0)-前移到有效节点后并将前面的改成signal
首节点可以是什么:空节点,当前正获取锁节点,曾经获取锁的节点(老二还没醒,老二醒来但被人抢了,没老二),可见首节点一定是有效的(<=0)