今天就来一点一点剖析AQS的源码,废话不多说,直接撸码!
AQS的核心源码在 AbstractQueuedSynchronizer 类,先来总体了解一下这个是干嘛的呢?
-
提供一个框架来实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关的同步器(信号量、事件等)。此类旨在为大多数依赖单个原子 {@code int} 值来表示状态的同步器提供有用的基础。子类必须定义更改此状态的受保护方法,并定义该状态在获取或释放此对象方面的含义。鉴于这些,此类中的其他方法执行所有排队和阻塞机制。子类可以维护其他状态字段,但仅跟踪使用方法 {@link getState}、{@link setState} 和 {@link compareAndSetState} 操作的原子更新的 {@code int} 值以进行同步。
-
应将子类定义为非公共的内部帮助类,用于实现其封闭类的同步属性。{@code AbstractQueuedSynchronizer} 类不实现任何同步接口。相反,它定义了诸如 {@link acquireInterruptibly} 之类的方法,具体的锁和相关的同步器可以适当地调用这些方法来实现它们的公共方法。
-
此类支持默认的独占模式和共享模式中的一种或两种。以独占模式获取时,其他线程尝试获取时不会成功。多个线程的共享模式获取可能(但不一定)成功。此类不“理解”这些差异,除非在机械意义上,当共享模式获取成功时,下一个等待线程(如果存在)也必须确定它是否也可以获取。不同模式下等待的线程共享同一个FIFO队列。通常,实现子类仅支持其中一种模式,但两者都可以发挥作用,例如在 {@link ReadWriteLock} 中。只支持独占或只支持共享模式的子类不需要定义支持未使用模式的方法。
-
该类定义了一个嵌套的 {@link ConditionObject} 类,它可以被支持独占模式的子类用作 {@link Condition} 实现,其中方法 {@link isHeldExclusively} 报告是否针对当前独占模式保持同步线程,使用当前 {@link getState} 值调用的方法 {@link release} 完全释放此对象,并且 {@link acquire} 给定此保存的状态值,最终将此对象恢复到其先前获取的状态。否则没有 {@code AbstractQueuedSynchronizer} 方法会创建这样的条件,因此如果无法满足此约束,请不要使用它。{@link ConditionObject} 的行为当然取决于其同步器实现的语义。
此类为内部队列提供检查、检测和监控方法,以及用于条件对象的类似方法。这些可以根据需要使用 {@code AbstractQueuedSynchronizer} 导出到类中,以实现它们的同步机制。
此类的序列化仅存储底层原子整数维护状态,因此反序列化对象具有空线程队列。需要可序列化的典型子类将定义一个 {@code readObject} 方法,该方法在反序列化时将其恢复到已知的初始状态。
用法:要将此类用作同步器的基础,请在适用时重新定义以下方法,通过使用 {@link getState}、{@link setState} 和或 { 检查和或修改同步状态@link compareAndSetState}:
{@link #tryAcquire}
{@link #tryRelease}
{@link #tryAcquireShared}
{@link #tryReleaseShared}
{@link #isHeldExclusively}
-
默认情况下,这些方法中的每一个都会引发
{@link UnsupportedOperationException}。这些方法的实现必须是内部线程安全的,并且通常应该是短的而不是阻塞的。定义这些方法是 only支持的使用此类的方法。所有其他方法都被声明为 {@code final} 因为它们不能独立变化。
-
您可能还会发现从 {@link AbstractOwnableSynchronizer} 继承的方法对于跟踪拥有独占同步器的线程很有用。鼓励您使用它们——这使监视和诊断工具能够帮助用户确定哪些线程持有锁。
-
即使此类基于内部 FIFO 队列,它也不会自动强制执行 FIFO 获取策略。独占同步的核心形式为:
获得:
while (!tryAcquire(arg)) {
enqueue thread if it is not already queued;
possibly block current thread;
}
释放:
if (tryRelease(arg))
unblock the first queued thread;
-
(共享模式类似,但可能涉及级联信号。)
-
因为在入队之前调用acquire中的检查,新的获取线程可能插入在其他被阻塞和排队的线程之前。但是,如果需要,您可以定义 {@code tryAcquire} 和或 {@code tryAcquireShared} 通过内部调用一种或多种检查方法来禁用插入,从而提供公平的FIFO 获取顺序。特别是,如果 {@link hasQueuedPredecessors}(一种专门为公平同步器使用的方法)返回 {@code true},大多数公平同步器可以定义 {@code tryAcquire} 返回 {@code false}。其他变化是可能的。
-
默认插入的吞吐量和可扩展性通常最高战略。虽然这不能保证公平或无饥饿,但允许较早排队的线程在稍后排队的线程之前重新竞争,并且每次重新竞争都有成功对抗传入线程的无偏机会。此外,虽然获取不是通常意义上的“旋转”,但它们可能会在阻塞之前执行多个 {@code tryAcquire} 调用,并与其他计算穿插。当独占同步只是短暂地保持时,这提供了自旋的大部分好处,而没有大部分责任。如果需要,您可以通过预先调用获取具有“快速路径”检查的方法来增加这一点,可能会预先检查 {@link hasContended} 和或 {@link hasQueuedThreads} 仅在同步器可能不会被争用时才这样做。
-
此类为同步提供了高效且可扩展的基础,部分原因是将其使用范围专门用于可以依赖 {@code int} 状态、获取和释放参数以及内部 FIFO 等待队列的同步器。如果这还不够,您可以使用 {@link java.util.concurrent.atomic atomic} 类、您自己的自定义 {@link java.util.Queue} 类和 {@link LockSupport} 阻塞从较低级别构建同步器支持。
使用示例:
这里是一个不可重入互斥锁类,它用值0表示解锁状态,1表示锁定状态。虽然不可重入锁并不严格要求记录当前所有者线程,但无论如何,此类都会这样做以使使用情况更易于监控。它还支持条件并公开一种检测方法:
class Mutex implements Lock, java.io.Serializable {
// 我们的内部帮助类
private static class Sync extends AbstractQueuedSynchronizer {
// 报告是否处于锁定状态
protected boolean isHeldExclusively() {
return getState() == 1;
}
// 如果状态为零,则获取锁
public boolean tryAcquire(int acquires) {
assert acquires == 1; // 否则未使用
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 通过将状态设置为零来释放锁
protected boolean tryRelease(int releases) {
assert releases == 1; // 否则未使用
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// 提供条件
Condition newCondition() { return new ConditionObject(); }
//正确反序列化
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // 重置为解锁状态
}
}
// 同步对象完成所有艰苦的工作。我们只是转发它。
private final Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public boolean tryLock() { return sync.tryAcquire(1); }
public void unlock() { sync.release(1); }
public Condition newCondition() { return sync.newCondition(); }
public boolean isLocked() { return sync.isHeldExclusively(); }
public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}}
这是一个类似于 {@link java.util.concurrent.CountDownLatch CountDownLatch} 的闩锁类,只是它只需要一个 {@code 信号} 即可触发。因为锁存器是非独占的,所以它使用 {@code shared} 获取和释放方法。
class BooleanLatch {
private static class Sync extends AbstractQueuedSynchronizer {
boolean isSignalled() { return getState() != 0; }
protected int tryAcquireShared(int ignore) {
return isSignalled() ? 1 : -1;
}
protected boolean tryReleaseShared(int ignore) {
setState(1);
return true;
}
}
private final Sync sync = new Sync();
public boolean isSignalled() { return sync.isSignalled(); }
public void signal() { sync.releaseShared(1); }
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
}}