-
CANCELLED:值为1,在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该Node的结点,其结点的waitStatus为CANCELLED,即结束状态,进入该状态后的结点将不会再变化。
-
SIGNAL:值为-1,被标识为该等待唤醒状态的后继结点,当其前继结点的线程释放了同步锁或被取消,将会通知该后继结点的线程执行。说白了,就是处于唤醒状态,只要前继结点释放锁,就会通知标识为SIGNAL状态的后继结点的线程执行。
-
CONDITION:值为-2,与Condition相关,该标识的结点处于等待队列中,结点的线程等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁。
-
PROPAGATE:值为-3,与共享模式相关,在共享模式中,该状态标识结点的线程处于可运行状态。
-
0状态:值为0,代表初始化状态。
1、acquire(int arg) 独占式获取同步状态,会调用重写的tryAcquire(int arg)方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//tryAcquire()自定义同步器获取同步状态的实现方法
2、addWaiter(Node mode) 如果同步状态获取失败,则构造同步节点(Node.EXCLUSIVE),并通过addWaiter(Node node)方法将改节点加入到同步队列尾部
private Node addWaiter(Node mode) {
//以给定模式构造节点。mode有两种:EXCLUSIVE(独占)和SHARED(共享)
Node node = new Node(Thread.currentThread(), mode);
// 尝试快速在尾部添加;
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//上一步失败则通过enq入队
enq(node);
return node;
}
3、enq() 尾部快速添加不成功,则再次利用“死循环”的方式将节点添加到同步队列尾部。
//CAS"自旋",直到成功加入队尾
private Node enq(final Node node) {
for (;;) {
Node t = tail;
// 队列为空,创建一个空的标志结点作为head结点,并将tail也指向它。
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
//将tail指向head节点
tail = head;
} else {
//1.将新节点的前置指针指向链表尾部节点
node.prev = t;
//2.通过CAS将tail的引用指向node
if (compareAndSetTail(t, node)) {
//3.将原尾部节点的后置指针指向新节点
t.next = node;
//for循环出口
return t;
}
}
}
}
4、acquireQueued(Node node,int arg) 使该节点以“死循环”的方式获取不同状态。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true; //标记是否成功拿到资源
try {
boolean interrupted = false; //标记等待过程中是否被中断过
for (;;) {
final Node p = node.predecessor(); //拿到前驱节点
//1.如果前驱是head,即该结点已成第二节点,
//2.那么便有资格去尝试获取资源(tryAcquire(arg))。
if (p == head && tryAcquire(arg)) {
//获取同步状态成功,指定为新的head节点
setHead(node);
//切断原head与node节点队列的链接
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
5、shouldParkAfterFailedAcquire(Node, Node)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
//值为-1,后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,是后继节点的线程得意运行
return true;
if (ws > 0) {
/*
* 前驱节点被取消(CANCELLED),一直往前找,找到最近的一个未被取消(CANCELLED)的节点
* 并放弃那些被跳过的节点。随后被放弃的节点会被GC。
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//如果前驱正常,那就把前驱的状态设置成SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
6、parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);//调用park()使线程进入waiting状态
return Thread.interrupted();//如果被唤醒,查看自己是不是被中断的。
}
总结acquireQueued(Node node,int arg) 该函数的具体流程:
- 结点进入队尾后,检查状态,找到安全休息点;
- 调用park()进入waiting状态,等待unpark()或interrupt()唤醒自己;
- 被唤醒后,看自己是不是有资格获取同步状态。如果获取了同步状态,head指向当前结点,并返回从入队到获取同步状态的整个过程中是否被中断过;如果没拿到,继续流程1。
总结acquire(int arg)的流程
- 调用自定义同步器的tryAcquire()尝试直接去获取同步状态,如果成功则直接返回;
- 没成功,则addWaiter()将该线程生成节点(Node)加入等待队列的尾部,并标记为独占模式;
- acquireQueued()使线程在等待队列中休息,等待前驱节点调用release(int arg)方法释放资源并且唤醒(unpark())其后继节点。后继节点会去尝试获取资源。获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
- 如果线程在等待过程中被中断过,它是不响应的;只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。