【深入学习并发之三】AQS:Abstract Queed Synchronizer源码分析

若阅读过程中出现疑问,可先阅读并发学习总览

Abstract Queed Synchronizer 源码分析

一、AQS是什么

我们先来看看Doug Lea(JUC包编写者)留下的类前注释:

Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues.

简而言之,为JUC包中用得到阻塞队列的阻塞锁和相关同步工具提供一个框架。

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.

主要意思就是,AQS带有一个具有原子性的值,state,应用AQS的同步工具(如ReentrantLock)都依赖于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.

意思是,AQS的实现类(一般为各种同步工具)通过重写AQS的protected方法来变更这个state,同时定义state不同值以及其变更的意义(资源被获取或释放)。

Given these, the other methods in this class carry out all queuing and blocking mechanics.

基于以上的设计,在这个类中其他的方法(未改变state的方法)实现了所有队列机制和阻塞机制。

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.

子类可以维护其他state字段,但是只有使用getState、setState和compareAndSetState方法操作的state值才会被进行同步。

上面的是第一段,是AQS的一个总览,做一下总结:
  • AQS维护了一个原子值state,实现类通过改变state来表示资源的当前状态
  • 只有getter、setter和compareAndSetState三个方法可以变更“官方”state值
  • 其他的方法实现了阻塞机制和队列机制

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.

上面整段的意思是,AQS没有实现任何同步接口,而是定义了相关的一些抽象方法(如acquireInterruptibly)供其实现类调用。同时,AQS的实现类应当是同步工具的内部辅助类(如ReentrantLock的内部类Sync),来帮助实现同步功能。

This class supports either or both a default exclusive mode and a shared 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 "understand" 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.

上面这段介绍了共享模式和独占模式,只有两个区别:
1 独占模式下资源不可被其他线程获取,共享模式则不然;
2 共享模式下,线程获取资源成功后,下个线程需要确认自己是不是也可以得到资源
这两个模式下所用的FIFO队列是同一个,且子类只需要实现其中一种,未涉及到的方法可以不做实现。

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.

这段说的是AQS内置了一个ConditionObject类,实现了Condition接口。(笔者注:Condition其实就是提供了类似Object.wait/signal的工具类,实现形式是一个用AQS.Node组成的队列。当前线程持有资源时,可以通过condition.wait()进入condition队列并挂起,被signal后进入AQS的等待队列继续进行资源等待和争夺。)Condition类的存在是为了提供条件队列的功能。

二、AQS的工作原理和流程//10.16 10.17

AQS类内由以下几模块的内容组成:

  • 核心成员变量与方法
  • 改变队列相关的方法
  • “获取”与“释放”相关的方法
  • 不同模式下,各个版本的“获取”与“释放”
  • 留给子类实现的“出口”方法
  • 队列检查相关的方法
  • condition相关的方法

我们重点看一下核心成员变量与方法,同时通过留给子类实现的出口方法,套入各个使用AQS的类,来解析它们使用AQS的异同。其他的内容,稍微混个脸熟即可。

1)AQS核心成员变量与方法

作为一个同步队列类工具,AQS存在一个内部类来实现对线程的封装,同时设定了指向队列头和尾的两个指针。
结合上文分析,AQS是通过对state进行改变来实现更新同步状态的,所以state和相关的getter、setter必不可少。

1’Node类
        /** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;

Doug Lea用了两个常量表示一个Node(线程)处于共享模式还是独占模式。若处于共享模式,则指向一个仅初始化后的Node;若处于独占模式,则指向一个为空的Node。

        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;

        /**
         * Status field, taking on only the values:
         *   SIGNAL:     The successor of this node is (or will soon be)
         *               blocked (via park), so the current node must
         *               unpark its successor when it releases or
         *               cancels. To avoid races, acquire methods must
         *               first indicate they need a signal,
         *               then retry the atomic acquire, and then,
         *               on failure, block.
         *   CANCELLED:  This node is cancelled due to timeout or interrupt.
         *               Nodes never leave this state. In particular,
         *               a thread with cancelled node never again blocks.
         *   CONDITION:  This node is currently on a condition queue.
         *               It will not be used as a sync queue node
         *               until transferred, at which time the status
         *               will be set to 0. (Use of this value here has
         *               nothing to do with the other uses of the
         *               field, but simplifies mechanics.)
         *   PROPAGATE:  A releaseShared should be propagated to other
         *               nodes. This is set (for head node only) in
         *               doReleaseShared to ensure propagation
         *               continues, even if other operations have
         *               since intervened.
         *   0:          None of the above
         *
         * The values are arranged numerically to simplify use.
         * Non-negative values mean that a node doesn't need to
         * signal. So, most code doesn't need to check for particular
         * values, just for sign.
         *
         * The field is initialized to 0 for normal sync nodes, and
         * CONDITION for condition nodes.  It is modified using CAS
         * (or when possible, unconditional volatile writes).
         */
        volatile int waitStatus;

waitStatus是Node结点的等待状态,用于表明结点当前的状态。 其取值为:
CANCELLED = 1;即当前结点的调度已取消;
SIGNAL = -1;表明当前结点的后继结点在等待当前节点唤醒,后继结点入队时,会将其前驱结点的状态更新为SIGNAL;
CONDITION = -2;表明当前结点等待在条件队列Condition中,若此结点被signal后,则从条件队列中转移到同步队列中,竞争资源。
PROPAGATE = -3;共享模式下专用,该结点在入队时,有可能唤醒其后继多个结点。
新节点入队的默认waitStatus是0。
由上我们可以得知,结点正常竞争资源的状态都是<=0的,而CANCELLED状态=1>0

        /**
         * Link to predecessor node that current node/thread relies on
         * for checking waitStatus. Assigned during enqueuing, and nulled
         * out (for sake of GC) only upon dequeuing.  Also, upon
         * cancellation of a predecessor, we short-circuit while
         * finding a non-cancelled one, which will always exist
         * because the head node is never cancelled: A node becomes
         * head only as a result of successful acquire. A
         * cancelled thread never succeeds in acquiring, and a thread only
         * cancels itself, not any other node.
         */
        volatile Node prev;

        /**
         * Link to the successor node that the current node/thread
         * unparks upon release. Assigned during enqueuing, adjusted
         * when bypassing cancelled predecessors, and nulled out (for
         * sake of GC) when dequeued.  The enq operation does not
         * assign next field of a predecessor until after attachment,
         * so seeing a null next field does not necessarily mean that
         * node is at end of queue. However, if a next field appears
         * to be null, we can scan prev's from the tail to
         * double-check.  The next field of cancelled nodes is set to
         * point to the node itself instead of null, to make life
         * easier for isOnSyncQueue.
         */
        volatile Node next;

prev是当前结点的前驱结点,在入队时分配,在出队时设置为null(出于GC的考虑)。当结点的前驱结点取消竞争时,当前结点会自旋找到下一个前驱结点,保证一定能找到,因为头结点一定不被取消,这是因为1)取消竞争的线程不能成功获取资源,变身头结点2)线程只能自我取消
next是当前结点的后继结点,在入队时分配,在前驱结点取消过程中调整,在出队时设置为null。enq操作直到“attachment”(原谅在下才疏学浅,理解不了这个是什么操作,可能看到后面会理解)后才分配后继结点,所以后继结点为null的结点并不一定是末尾结点。取消竞争的结点的next被设置为自己本身,方便isOnSyncQueue方法工作。

        /**
         * The thread that enqueued this node.  Initialized on
         * construction and nulled out after use.
         */
        volatile Thread thread;

        /**
         * Link to next node waiting on condition, or the special
         * value SHARED.  Because condition queues are accessed only
         * when holding in exclusive mode, we just need a simple
         * linked queue to hold nodes while they are waiting on
         * conditions. They are then transferred to the queue to
         * re-acquire. And because conditions can only be exclusive,
         * we save a field by using special value to indicate shared
         * mode.
         */
        Node nextWaiter;
        
         /**
         * Returns true if node is waiting in shared mode.
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

thread即为被包装的竞争线程,这个很好理解。
nextWaiter是条件队列Condition里的后继结点,在独占模式下(exclusive mode),指向Condition队列中的下个结点;在共享模式下,不存在Condition条件队列,所以指向上文的常量SHARED结点,表示处于共享模式下。
isShared方法即为判断当前结点是独占模式还是共享模式。

        /**
         * Returns previous node, or throws NullPointerException if null.
         * Use when predecessor cannot be null.  The null check could
         * be elided, but is present to help the VM.
         *
         * @return the predecessor of this node
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }

返回前驱结点的方法和三种构造方法,其中
空构造方法用于建立头结点或SHARED标志结点
addWaiter需要在创建时指定mode,使用第二个方法
Condition要指定waitStatus,要使用第三个方法

2’Node类型的head与tail
    /**
     * Head of the wait queue, lazily initialized.  Except for
     * initialization, it is modified only via method setHead.  Note:
     * If head exists, its waitStatus is guaranteed not to be
     * CANCELLED.
     */
    private transient volatile Node head;

    /**
     * Tail of the wait queue, lazily initialized.  Modified only via
     * method enq to add new wait node.
     */
    private transient volatile Node tail;

在同步队列中,AQS使用两个引用,head和tail来标志队列。
head结点只能被setHead方法设定和更改,且head结点的waitStatus不可为CANCELLED
tail结点只能被enq方法更改。

3’state(状态量)
4’getState()、setState(int newState)
5’compareAndSetState(int expect, int update)

三、哪些JUC包中的工具用到了AQS

//ReentrantLock
//Semaphore
//CountdownLatch
//…
//分别介绍各种使用方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值