同步队列
AQS依赖内部的同步队列(FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器将会将当前线程以及等待状态等信息构造成为一个节点(NODE)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
独占式同步状态获取与释放
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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);
}
}
完成了同步状态获取,节点构造,加入同步队列以及在同步队列中自旋等待。
首先调用自定义同步器实现的tryAcquire(int arg)方法,该方法保证线程安全的获取同步状态,如果同步状态获取失败,则构造同步节点(独占式Node.EXCLUSIVE),同一时刻只能有一个线程成功获取同步状态,并通过addWaiter()方法将该节点加入到同步队列的尾部,最后调用acquireQueued()方法,使该节点以“死循环“方式获取同步状态。如果获取不到则阻塞节点中的线程,而被阻塞线程的唤醒主要依靠前驱节点的出队或阻塞线程被中断来实现。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
会唤醒头节点的后继节点
共享式同步状态获取与释放
共享是获取与独占式获取最主要的区别在于同一时刻能否有多个线程同时获取到同步状态。
左半部分,共享式访问资源时,其他共享式的访问军备允许,而独占式访问被阻塞
右半部分,独占式访问资源时,同一时刻其他访问均被阻塞。