AbstractQueuedSynchronizer-最基础的同步器模板
它是一个框架,一个模板
AQS只是一个框架,具体资源的获取/释放方式(tryAcquire/tryRealease)交由自定义同步器去实现,是一个抽象的模板类,体现了模板方法的设计模式。
核心变量state
/**
* The synchronization state.
*/
private volatile int state;
复制代码
AQS甚至于concurrent包中的所有类,都是在对volatile变量读写结合CAS操作的基础上实现的。
可以重写的方法(没有限定final):
//独占式获取同步状态,需要CAS更新同步状态
protected boolean tryAcquire(int arg);
//独占式释放同步状态
protected boolean tryRealease(int arg);
//共享式获取同步状态,返回值大于0表示成功,反之失败
protected boolean tryAcquireShared(int arg);
//共享式释放同步状态
protected boolean tryRealeaseShared(int arg);
//当前同步器是否在独占模式下被线程占用
protected boolean isHeldExclusively();
复制代码
- 这些方法都没有直接声明成abstract,而是直接抛出异常。也就意味着不是每个子类都必须实现这些方法,比如独占锁就完全不需要去实现和它不相关的shared方法。
不可重写的方法(直接声明final):
public final void acquire(int arg);
public final void acquireInterruptibly(int arg);
public final void acquireShared(int arg);
public final void acquireSharedInterruptibly(int arg);
public final boolean tryAcquireNanos(int arg, long nanos);
public final boolean tryAcquireSharedNanos(int arg, long nanos);
public final boolean release(int arg);
public final boolean releaseShared(int arg);
public final Collection<Thread> getQueueThreads();
复制代码
独占式的同步状态获取流程
首先调用acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
复制代码
tryAcquire
成功则结束,直接返回,可以开始执行临界区的代码- 否则,要将当前的线程构建成一个
Node.EXCLUSIVE
模式的节点,加入到队列的末尾,并且调用acquireQueued
方法,让这个节点进入自旋状态。也就是说,这个节点加入同步队列后,就会死循环的去获取同步状态,从而阻塞在了临界区的外面。该线程的唤醒主要依靠前驱结点的出队或者阻塞线程被中断。
private Node addWaiter(Node mode) {
/**
* 以一个Node类的静态变量Node.EXCLUSIVE/Node.SHARED
* 作为mode标志节点的模式
* mode节点会被设置成nextWaiter
* 所以nextWaiter可用来判断节点的两种模式
*/
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;
}
复制代码
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);
}
}
复制代码
独占式同步状态的释放
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
复制代码
独占式超时获取同步状态流程
超时获取同步状态原理比较类似。
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
复制代码
共享式同步状态的获取
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
复制代码
虽然是共享式的获取同步状态,但也不意味着这个同步状态可以被无限的获取,还是有一个限制的。例如Semaphore中的permits即是一个限制。
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
复制代码
当remaining < 0 时,就会触发tryAcquireShared(arg) < 0, 也就是尝试共享式的获取失败。 那么这时候也会像独占式时那样,加入同步队列,自旋的去获取同步状态。也就是超过state允许的最大值后,也不能去执行临界区的代码。 唯一的区别就是,将当前线程封装成了一个Node.SHARED模式的节点。在自旋状态中尝试获取成功后,会重新设置头节点并且将这个影响借助链表中的节点以及节点的waitStatus传播下去。
private void doAcquireShared(int arg) {
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);
}
}
复制代码
LockSupport工具包
以上方法中关于对线程的阻塞和唤醒主要通过LockSupport这个包中的几个方法来实现。
- void park() : 阻塞当前线程,线程不再参与处理器调度,如果调用unpark方法或者当前线程被中断,才能从park()中返回。
- void parkNanos(long nanos): 增加超时返回的功能。
- void parkUntil(long deadline):将线程阻塞到deadline时间。
Doug Lea 为了演示这个类的基本功能,不借助aqs实现了一个先进先出的互斥锁。
class FIFOMutex {
private final AtomicBoolean locked = new AtomicBoolean(false);
private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();
public void lock() {
boolean wasInterrupted = false;
Thread current = Thread.currentThread();
waiters.add(current);
// Block while not first in queue or cannot acquire lock
while (waiters.peek() != current ||
!locked.compareAndSet(false, true)) {
LockSupport.park(this);
if (Thread.interrupted()) // ignore interrupts while waiting
wasInterrupted = true;
}
waiters.remove();
if (wasInterrupted) // reassert interrupt status on exit
current.interrupt();
}
public void unlock() {
locked.set(false);
LockSupport.unpark(waiters.peek());
}
}
复制代码