并发编程原理与实战(三十二)AQS核心机制与使用示例解读

前面的系列文章,我们主要学习了线程基础、线程并发协同工具类、线程安全、锁、原子类等内容。有了前面的这些知识,接下来我们来学习Java并发编程中线程协作工具统一的底层框架AbstractQueuedSynchronizer,简称AQS。

认识AQS

首先,AbstractQueuedSynchronizer是一个抽象类。

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    ...
}

AQS通过抽象队列式同步机制,为ReentrantLock、Semaphore等并发工具提供了统一的底层框架。这些工具虽然功能各异(如独占锁、信号量等),但都需要解决线程阻塞、唤醒及资源状态管理的共性问题。AQS将这类共性逻辑(如队列管理、状态原子操作)封装为通用模板,避免了每个工具类重复实现这些复杂逻辑。既然是一个抽象类,那么就有子类去实现,官方对该类的子类的实现做出了详细说明。

子类实现规范

/**
 * Provides a framework for implementing blocking locks and related
 * synchronizers (semaphores, events, etc) that rely on
 * first-in-first-out (FIFO) wait queues.  This class is designed to
 * be a useful basis for most kinds of synchronizers that rely on a
 * single atomic {@code int} value to represent state. Subclasses
 * must define the protected methods that change this state, and which
 * define what that state means in terms of this object being acquired
 * or released.  Given these, the other methods in this class carry
 * out all queuing and blocking mechanics. Subclasses can maintain
 * other state fields, but only the atomically updated {@code int}
 * value manipulated using methods {@link #getState}, {@link
 * #setState} and {@link #compareAndSetState} is tracked with respect
 * to synchronization.
 */

本类为基于先进先出(FIFO)等待队列的阻塞锁及相关同步器(信号量、事件等)提供实现框架。该类设计适用于大多数依赖单个原子整型变量表示状态的同步器场景。子类需通过以下方式实现定制化:

(1)状态定义:子类必须实现受保护的方法来修改状态变量,并定义该状态值在对象获取/释放操作中的具体含义(如0表示未锁定,1表示锁定)‌。

(2)核心机制:本类提供所有队列管理和阻塞机制的实现,包括:等待队列的FIFO调度,线程阻塞/唤醒控制,原子状态变更操作。

(3)状态管理规范:仅通过getState()、setState()和compareAndSetState()方法操作的原子整型变量受同步机制保护。

 /* <p>Subclasses should be defined as non-public internal helper
 * classes that are used to implement the synchronization properties
 * of their enclosing class.  Class
 * {@code AbstractQueuedSynchronizer} does not implement any
 * synchronization interface.  Instead it defines methods such as
 * {@link #acquireInterruptibly} that can be invoked as
 * appropriate by concrete locks and related synchronizers to
 * implement their public methods.
 */

子类应定义为非公开的内部辅助类,用于实现其外围类的同步特性。类AbstractQueuedSynchronizer并未实现任何同步接口。相反,它定义了诸如acquireInterruptibly等方法,这些方法可由具体的锁及相关同步器在需要时调用,以实现其公共方法。

独占或共享模式

 /* <p>This class supports either or both a default <em>exclusive</em>
 * mode and a <em>shared</em> mode. When acquired in exclusive mode,
 * attempted acquires by other threads cannot succeed. Shared mode
 * acquires by multiple threads may (but need not) succeed. This class
 * does not &quot;understand&quot; these differences except in the
 * mechanical sense that when a shared mode acquire succeeds, the next
 * waiting thread (if one exists) must also determine whether it can
 * acquire as well. Threads waiting in the different modes share the
 * same FIFO queue. Usually, implementation subclasses support only
 * one of these modes, but both can come into play for example in a
 * {@link ReadWriteLock}. Subclasses that support only exclusive or
 * only shared modes need not define the methods supporting the unused mode.
 */

本类支持独占模式、共享模式或两者的组合。在独占模式下被获取时,其他线程的获取尝试将无法成功。共享模式下,多个线程的获取可能(但不一定)成功。此类仅在机械意义上理解这些差异:当共享模式获取成功时,下一个等待线程(如果存在)也必须判断自身是否也能获取。不同模式下的等待线程共享相同的FIFO队列。通常,实现子类仅支持其中一种模式,但两者可能同时发挥作用(例如在ReadWriteLock中)。仅支持独占或共享模式的子类无需定义未使用模式的相关方法。

Condition实现

 /* <p>This class defines a nested {@link ConditionObject} class that
 * can be used as a {@link Condition} implementation by subclasses
 * supporting exclusive mode for which method {@link
 * #isHeldExclusively} reports whether synchronization is exclusively
 * held with respect to the current thread, method {@link #release}
 * invoked with the current {@link #getState} value fully releases
 * this object, and {@link #acquire}, given this saved state value,
 * eventually restores this object to its previous acquired state.  No
 * {@code AbstractQueuedSynchronizer} method otherwise creates such a
 * condition, so if this constraint cannot be met, do not use it.  The
 * behavior of {@link ConditionObject} depends of course on the
 * semantics of its synchronizer implementation.
 */

本类定义了嵌套的ConditionObject类,对于支持独占模式的子类,可用作Condition实现。其中方法isHeldExclusively用于报告当前线程是否独占持有同步资源,方法release配合当前getState值可完全释放本对象,而acquire(给定保存的状态值)最终会将本对象恢复至先前获取状态。若此约束无法满足,请勿使用此条件。ConditionObject的行为当然取决于同步器实现的具体语义。

类功能说明与序列化特性

 /* <p>This class provides inspection, instrumentation, and monitoring
 * methods for the internal queue, as well as similar methods for
 * condition objects. These can be exported as desired into classes
 * using an {@code AbstractQueuedSynchronizer} for their
 * synchronization mechanics.
 *
 * <p>Serialization of this class stores only the underlying atomic
 * integer maintaining state, so deserialized objects have empty
 * thread queues. Typical subclasses requiring serializability will
 * define a {@code readObject} method that restores this to a known
 * initial state upon deserialization.
 */

该类提供对内部队列的检查、工具化及监控方法,同时也为条件对象提供类似功能。这些方法可根据需求导出到使用AbstractQueuedSynchronizer作为同步机制的类中。

本类的序列化仅存储维持状态的底层原子整数,反序列化后的对象将拥有空的线程队列。通常需要可序列化的子类会定义readObject方法,在反序列化时将状态恢复到已知初始状态。

使用方法

 /* <h2>Usage</h2>
 *
 * <p>To use this class as the basis of a synchronizer, redefine the
 * following methods, as applicable, by inspecting and/or modifying
 * the synchronization state using {@link #getState}, {@link
 * #setState} and/or {@link #compareAndSetState}:
 *
 * <ul>
 * <li>{@link #tryAcquire}
 * <li>{@link #tryRelease}
 * <li>{@link #tryAcquireShared}
 * <li>{@link #tryReleaseShared}
 * <li>{@link #isHeldExclusively}
 * </ul>
 
 * Each of these methods by default throws {@link
 * UnsupportedOperationException}.  Implementations of these methods
 * must be internally thread-safe, and should in general be short and
 * not block. Defining these methods is the <em>only</em> supported
 * means of using this class. All other methods are declared
 * {@code final} because they cannot be independently varied.
 */

若要将此类用作同步器的基础,需通过getState、setState和compareAndSetState方法检查和/或修改同步状态,并重新定义以下适用方法:

  • tryAcquire
  • tryRelease
  • tryAcquireShared
  • tryReleaseShared
  • isHeldExclusively

这些方法的默认实现均会抛出UnsupportedOperationException异常。这些方法的实现必须保证内部线程安全,且通常应保持简短且不阻塞。定义这些方法是使用本类的‌唯一‌支持方式。其他方法均声明为final,因为它们无法独立变化‌。

 /* <p>You may also find the inherited methods from {@link
 * AbstractOwnableSynchronizer} useful to keep track of the thread
 * owning an exclusive synchronizer.  You are encouraged to use them
 * -- this enables monitoring and diagnostic tools to assist users in
 * determining which threads hold locks.
 *
 * <p>Even though this class is based on an internal FIFO queue, it
 * does not automatically enforce FIFO acquisition policies.  The core
 * of exclusive synchronization takes the form:
 */

你可能还会发现从AbstractOwnableSynchronizer继承的方法对于跟踪拥有独占同步器的线程很有用。建议你使用这些方法 - 这使监控和诊断工具能够帮助用户确定哪些线程持有锁。

虽然此类基于内部FIFO队列实现,但它不会自动强制执行FIFO获取策略。独占同步的核心实现形式为:

//Acquire:
while (!tryAcquire(arg)) {
    //如果线程尚未入队,则将其入队;
    //可能阻塞当前线程。
}
//Release:
if (tryRelease(arg)) {
   //解除第一个排队线程的阻塞; 
}

加塞策略

 /* <p id="barging">Because checks in acquire are invoked before
 * enqueuing, a newly acquiring thread may <em>barge</em> ahead of
 * others that are blocked and queued.  However, you can, if desired,
 * define {@code tryAcquire} and/or {@code tryAcquireShared} to
 * disable barging by internally invoking one or more of the inspection
 * methods, thereby providing a <em>fair</em> FIFO acquisition order.
 * In particular, most fair synchronizers can define {@code tryAcquire}
 * to return {@code false} if {@link #hasQueuedPredecessors} (a method
 * specifically designed to be used by fair synchronizers) returns
 * {@code true}.  Other variations are possible.
 */

由于获取操作中的检查在入队前执行,新获取线程可能会加塞到其他阻塞线程之前。但如需禁用加塞行为,可通过在内部调用一个或多个检查方法(如tryAcquire和/或tryAcquireShared),从而实现公平的FIFO获取顺序。具体而言,多数公平同步器可将tryAcquire定义为:当hasQueuedPredecessors(专为公平同步器设计的方法)返回true时,返回false。其他变体实现也是可能的。

 /* <p>Throughput and scalability are generally highest for the
 * default barging (also known as <em>greedy</em>,
 * <em>renouncement</em>, and <em>convoy-avoidance</em>) strategy.
 * While this is not guaranteed to be fair or starvation-free, earlier
 * queued threads are allowed to recontend before later queued
 * threads, and each recontention has an unbiased chance to succeed
 * against incoming threads.  Also, while acquires do not
 * &quot;spin&quot; in the usual sense, they may perform multiple
 * invocations of {@code tryAcquire} interspersed with other
 * computations before blocking.  This gives most of the benefits of
 * spins when exclusive synchronization is only briefly held, without
 * most of the liabilities when it isn't. If so desired, you can
 * augment this by preceding calls to acquire methods with
 * "fast-path" checks, possibly prechecking {@link #hasContended}
 * and/or {@link #hasQueuedThreads} to only do so if the synchronizer
 * is likely not to be contended.
 */

默认加塞策略(亦称贪婪放弃避让策略)通常能提供最高吞吐量和可扩展性。虽然这不保证绝对公平或无饥饿,但早期排队线程可在后期排队线程前重新竞争,且每次重新竞争对新来线程具有均等成功机会。此外,获取操作虽非传统意义上的自旋,但在阻塞前可能交替执行多次tryAcquire调用及其他计算。当独占同步仅短暂持有时,这能获得自旋的大部分优势;当不持有时,则规避了大部分弊端。如需增强此特性,可在获取方法调用前添加快速路径检查,可能预检hasContended和/或hasQueuedThreads,仅当同步器不太可能发生争用时执行。

高效可扩展的基础

 /* <p>This class provides an efficient and scalable basis for
 * synchronization in part by specializing its range of use to
 * synchronizers that can rely on {@code int} state, acquire, and
 * release parameters, and an internal FIFO wait queue. When this does
 * not suffice, you can build synchronizers from a lower level using
 * {@link java.util.concurrent.atomic atomic} classes, your own custom
 * {@link java.util.Queue} classes, and {@link LockSupport} blocking
 * support.
 */

此类通过将使用范围限定为依赖int状态、获取/释放参数及内部FIFO等待队列的同步器,为同步操作提供了高效可扩展的基础。若此方案不足,可基于java.util.concurrent.atomic atomic类、自定义 java.util.Queue类及LockSupport阻塞支持构建更底层的同步器。

使用示例

以下是一个使用值0表示未锁定状态、1表示锁定状态的非可重入互斥锁类。虽然非可重入锁并不严格要求记录当前拥有线程,但本类仍然记录这些信息以便于监控使用情况。此外,该类还支持条件变量,并公开了一些监控方法:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

class Mutex implements Lock, java.io.Serializable {

    // 我们的内部辅助类
    private static class Sync extends AbstractQueuedSynchronizer {
        // 如果状态为零,则获取锁
        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 (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        // 报告是否处于锁定状态
        public boolean isLocked() {
            return getState() != 0;
        }

        public boolean isHeldExclusively() {
            // 数据竞争,但因‘凭空保证’而安全
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        // 提供条件变量
        public Condition newCondition() {
            return new ConditionObject();
        }

        // 正常反序列化
        private void readObject(ObjectInputStream s)
                throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // 重置为未锁定状态
        }
    }

    // The sync object does all the hard work. We just forward to it.
    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.isLocked();
    }

    public boolean isHeldByCurrentThread() {
        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));
    }
}

下面这个类与CountDownLatch类似,但仅需单次signal触发。由于门闩是非独占的,因此使用共享的acquire和release方法。

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);
    }
}

总结

AQS的核心设计模式是‌模板方法模式‌,AQS将同步器的核心流程(如线程排队、阻塞唤醒)固化在父类中,仅将资源获取/释放的逻辑通过抽象方法(如tryAcquire、tryRelease)交给子类实现。基于AQS实现的同步器(锁实现类)核心规范:

(1)同步器(锁实现类)内部创建AbstractQueuedSynchronizer的子类。

(2)子类通过继承父类的getState、setState和compareAndSetState方法检查修改同步状态state;,并重新定义以下模板方法:

  • tryAcquire
  • tryRelease
  • tryAcquireShared
  • tryReleaseShared
  • isHeldExclusively
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帧栈

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值