15 - AQS 源码分析

1. AQS 概念

1.1 AQS 原理

  AbstractQueuedSynchronizer 类如其名,抽象的队列式的同步器,AQS 定义了一套多线程访问 共享资源的同步器框架,许多同步类实现都依赖于它,如常用的 ReentrantLock / Semaphore / CountDownLatch。AbstractQueuedSynchronizer 是 Java 并发包 java.util.concurrent 的核心基础组件,是实现 Lock 的基础。它维护了一个 volatile int state(代表共享资源)和一个 FIFO 线程等待队列(多线程争用资源被 阻塞时会进入此队列)。这里 volatile 是核心关键词。

在这里插入图片描述

  

1.2 AQS 资源共享方式

AQS 定义两种资源共享方式:

  1. Exclusive 独占资源-ReentrantLock,Exclusive(独占,只有一个线程能执行,如 ReentrantLock);
  2. Share 共享资源-Semaphore / CountDownLatch,Share(共享,多个线程可同时执行,如 Semaphore/CountDownLatch)。

  AQS 只是一个框架,具体资源的获取/释放方式交由自定义同步器去实现,AQS 这里只定义了一个 接口,具体资源的获取交由自定义同步器去实现了(通过 state 的 get/set/CAS)之所以没有定义成 abstract ,是因为独占模式下只用实现 tryAcquire-tryRelease ,而共享模式下只用实现 tryAcquireShared-tryReleaseShared。如果都定义成 abstract,那么每个模式也要去实现另一模式下的接口。

  不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/ 唤醒出队等),AQS 已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:

  1. isHeldExclusively():该线程是否正在独占资源。只有用到 condition 才需要去实现它;
  2. tryAcquire(int):独占方式。尝试获取资源,成功则返回 true,失败则返回 false;
  3. tryRelease(int):独占方式。尝试释放资源,成功则返回 true,失败则返回 false;
  4. tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0 表示成功,但没有剩余 可用资源;正数表示成功,且有剩余资源;
  5. tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回 true,否则返回 false;

  ReentrantReadWriteLock 实现独占和共享两种方式 。一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现 tryAcquire - tryRelease、tryAcquireShared - tryReleaseShared 中的一种即可。但 AQS 也支持自定义同步器同时实现独占和共享两种方式,如 ReentrantReadWriteLock。

  

2. AQS 源码分析

2.1 AQS 类结构
2.1.1 属性
	/**
     * 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;

    /**
     * The synchronization state.
     * 当前锁的状态:0 代表没有被占用
     * 			  大于 0 表示锁已经被占用(锁可以重入,每次重入都 +1)
     */
    private volatile int state;

	/**
     * The current owner of exclusive mode synchronization.
     * 继承自父类 AbstractOwnableSynchronizer 表示持有锁的线程
     */
    private transient Thread exclusiveOwnerThread;

  

2.1.2 方法
	锁状态相关方法
	/**
     * Returns the current value of synchronization state.
     * This operation has memory semantics of a {@code volatile} read.
     * @return current state value
     * 返回当前同步状态
     */
    protected final int getState() {
        return state;
    }

    /**
     * Sets the value of synchronization state.
     * This operation has memory semantics of a {@code volatile} write.
     * @param newState the new state value
     * 设置当前同步状态
     */
    protected final void setState(int newState) {
        state = newState;
    }

    /**
     * Atomically sets synchronization state to the given updated
     * value if the current state value equals the expected value.
     * This operation has memory semantics of a {@code volatile} read
     * and write.
     * 
     * 使用 CAS 设置当前同步状态,保证状态设置的原子性
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that the actual
     *         value was not equal to the expected value.
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }




	独占锁相关方法
	/**
     * Acquires in exclusive mode, ignoring interrupts.  Implemented
     * by invoking at least once {@link #tryAcquire},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquire} until success.  This method can be used
     * to implement method {@link Lock#lock}.
     * 
     * 独占式获取同步状态,如果获取失败则插入同步队列进行等待
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquire} but is otherwise uninterpreted and
     *        can represent anything you like.
     */
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

    /**
     * Acquires in exclusive mode, aborting if interrupted.
     * Implemented by first checking interrupt status, then invoking
     * at least once {@link #tryAcquire}, returning on
     * success.  Otherwise the thread is queued, possibly repeatedly
     * blocking and unblocking, invoking {@link #tryAcquire}
     * until success or the thread is interrupted.  This method can be
     * used to implement method {@link Lock#lockInterruptibly}.
     * 
     * 与 acquire(int arg) 相同,但是该方法响应中断
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquire} but is otherwise uninterpreted and
     *        can represent anything you like.
     * @throws InterruptedException if the current thread is interrupted
     */
    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

    /**
     * Attempts to acquire in exclusive mode, aborting if interrupted,
     * and failing if the given timeout elapses.  Implemented by first
     * checking interrupt status, then invoking at least once {@link
     * #tryAcquire}, returning on success.  Otherwise, the thread is
     * queued, possibly repeatedly blocking and unblocking, invoking
     * {@link #tryAcquire} until success or the thread is interrupted
     * or the timeout elapses.  This method can be used to implement
     * method {@link Lock#tryLock(long, TimeUnit)}.
     * 
     * 在 acquireInterruptibly(int arg) 基础上增加了超时等待功能,在超时时间
     * 内没有获取同步状态则返回false
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquire} but is otherwise uninterpreted and
     *        can represent anything you like.
     * @param nanosTimeout the maximum number of nanoseconds to wait
     * @return {@code true} if acquired; {@code false} if timed out
     * @throws InterruptedException if the current thread is interrupted
     */
    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }

    /**
     * Releases in exclusive mode.  Implemented by unblocking one or
     * more threads if {@link #tryRelease} returns true.
     * This method can be used to implement method {@link Lock#unlock}.
     * 
     * 独占式释放同步状态,该方法会在释放同步状态之后,将同步队列中的头节点的下
     * 一个节点包含的线程唤醒 
     *
     * @param arg the release argument.  This value is conveyed to
     *        {@link #tryRelease} but is otherwise uninterpreted and
     *        can represent anything you like.
     * @return the value returned from {@link #tryRelease}
     */
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }



	共享锁相关方法
	/**
     * Acquires in shared mode, ignoring interrupts.  Implemented by
     * first invoking at least once {@link #tryAcquireShared},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquireShared} until success.
     *  
     * 共享式获取同步状态,与独占式的区别在于同一时刻由多个线程获取同步状态
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquireShared} but is otherwise uninterpreted
     *        and can represent anything you like.
     */
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

    /**
     * Acquires in shared mode, aborting if interrupted.  Implemented
     * by first checking interrupt status, then invoking at least once
     * {@link #tryAcquireShared}, returning on success.  Otherwise the
     * thread is queued, possibly repeatedly blocking and unblocking,
     * invoking {@link #tryAcquireShared} until success or the thread
     * is interrupted.
     *  
     * 在 acquireShared() 方法基础上增加了能响应中断的功能 
     * 
     * @param arg the acquire argument.
     * This value is conveyed to {@link #tryAcquireShared} but is
     * otherwise uninterpreted and can represent anything
     * you like.
     * @throws InterruptedException if the current thread is interrupted
     */
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

    /**
     * Attempts to acquire in shared mode, aborting if interrupted, and
     * failing if the given timeout elapses.  Implemented by first
     * checking interrupt status, then invoking at least once {@link
     * #tryAcquireShared}, returning on success.  Otherwise, the
     * thread is queued, possibly repeatedly blocking and unblocking,
     * invoking {@link #tryAcquireShared} until success or the thread
     * is interrupted or the timeout elapses.
     *  
     * 在acquireSharedInterruptibly() 基础上增加了超时等待的功能
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquireShared} but is otherwise uninterpreted
     *        and can represent anything you like.
     * @param nanosTimeout the maximum number of nanoseconds to wait
     * @return {@code true} if acquired; {@code false} if timed out
     * @throws InterruptedException if the current thread is interrupted
     */
    public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquireShared(arg) >= 0 ||
            doAcquireSharedNanos(arg, nanosTimeout);
    }

    /**
     * Releases in shared mode.  Implemented by unblocking one or more
     * threads if {@link #tryReleaseShared} returns true.
     *  
     * 共享式释放同步状态
     *
     * @param arg the release argument.  This value is conveyed to
     *        {@link #tryReleaseShared} but is otherwise uninterpreted
     *        and can represent anything you like.
     * @return the value returned from {@link #tryReleaseShared}
     */
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }





	// Main exported methods
	AQS 使用模版方法设计模式,需要子类实现获取锁和释放锁的方法
    /**
     * Attempts to acquire in exclusive mode. This method should query
     * if the state of the object permits it to be acquired in the
     * exclusive mode, and if so to acquire it.
     *
     * <p>This method is always invoked by the thread performing
     * acquire.  If this method reports failure, the acquire method
     * may queue the thread, if it is not already queued, until it is
     * signalled by a release from some other thread. This can be used
     * to implement method {@link Lock#tryLock()}.
     *
     * <p>The default
     * implementation throws {@link UnsupportedOperationException}.
     *  
     * 独占式获取同步状态
     *
     * @param arg the acquire argument. This value is always the one
     *        passed to an acquire method, or is the value saved on entry
     *        to a condition wait.  The value is otherwise uninterpreted
     *        and can represent anything you like.
     * @return {@code true} if successful. Upon success, this object has
     *         been acquired.
     * @throws IllegalMonitorStateException if acquiring would place this
     *         synchronizer in an illegal state. This exception must be
     *         thrown in a consistent fashion for synchronization to work
     *         correctly.
     * @throws UnsupportedOperationException if exclusive mode is not supported
     */
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    /**
     * Attempts to set the state to reflect a release in exclusive
     * mode.
     *
     * <p>This method is always invoked by the thread performing release.
     * 
     * 独占式释放同步状态 
     * 
     * <p>The default implementation throws
     * {@link UnsupportedOperationException}.
     *
     * @param arg the release argument. This value is always the one
     *        passed to a release method, or the current state value upon
     *        entry to a condition wait.  The value is otherwise
     *        uninterpreted and can represent anything you like.
     * @return {@code true} if this object is now in a fully released
     *         state, so that any waiting threads may attempt to acquire;
     *         and {@code false} otherwise.
     * @throws IllegalMonitorStateException if releasing would place this
     *         synchronizer in an illegal state. This exception must be
     *         thrown in a consistent fashion for synchronization to work
     *         correctly.
     * @throws UnsupportedOperationException if exclusive mode is not supported
     */
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

    /**
     * Attempts to acquire in shared mode. This method should query if
     * the state of the object permits it to be acquired in the shared
     * mode, and if so to acquire it.
     *
     * <p>This method is always invoked by the thread performing
     * acquire.  If this method reports failure, the acquire method
     * may queue the thread, if it is not already queued, until it is
     * signalled by a release from some other thread.
     *
     * <p>The default implementation throws {@link
     * UnsupportedOperationException}.
     *  
     * 共享式获取同步状态
     *
     * @param arg the acquire argument. This value is always the one
     *        passed to an acquire method, or is the value saved on entry
     *        to a condition wait.  The value is otherwise uninterpreted
     *        and can represent anything you like.
     * @return a negative value on failure; zero if acquisition in shared
     *         mode succeeded but no subsequent shared-mode acquire can
     *         succeed; and a positive value if acquisition in shared
     *         mode succeeded and subsequent shared-mode acquires might
     *         also succeed, in which case a subsequent waiting thread
     *         must check availability. (Support for three different
     *         return values enables this method to be used in contexts
     *         where acquires only sometimes act exclusively.)  Upon
     *         success, this object has been acquired.
     * @throws IllegalMonitorStateException if acquiring would place this
     *         synchronizer in an illegal state. This exception must be
     *         thrown in a consistent fashion for synchronization to work
     *         correctly.
     * @throws UnsupportedOperationException if shared mode is not supported
     */
    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

    /**
     * Attempts to set the state to reflect a release in shared mode.
     *
     * <p>This method is always invoked by the thread performing release.
     *
     * <p>The default implementation throws
     * {@link UnsupportedOperationException}.
     *  
     * 共享式释放同步状态
     *
     * @param arg the release argument. This value is always the one
     *        passed to a release method, or the current state value upon
     *        entry to a condition wait.  The value is otherwise
     *        uninterpreted and can represent anything you like.
     * @return {@code true} if this release of shared mode may permit a
     *         waiting acquire (shared or exclusive) to succeed; and
     *         {@code false} otherwise
     * @throws IllegalMonitorStateException if releasing would place this
     *         synchronizer in an illegal state. This exception must be
     *         thrown in a consistent fashion for synchronization to work
     *         correctly.
     * @throws UnsupportedOperationException if shared mode is not supported
     */
    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

    /**
     * Returns {@code true} if synchronization is held exclusively with
     * respect to the current (calling) thread.  This method is invoked
     * upon each call to a non-waiting {@link ConditionObject} method.
     * (Waiting methods instead invoke {@link #release}.)
     *  
     * 当前线程是否是持有独占式的锁
     *
     * <p>The default implementation throws {@link
     * UnsupportedOperationException}. This method is invoked
     * internally only within {@link ConditionObject} methods, so need
     * not be defined if conditions are not used.
     *
     * @return {@code true} if synchronization is held exclusively;
     *         {@code false} otherwise
     * @throws UnsupportedOperationException if conditions are not supported
     */
    protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
    }

  

2.1.3 内部类
	同步队列的节点类
	/**
     * Wait queue node class.
     *
     * <p>The wait queue is a variant of a "CLH" (Craig, Landin, and
     * Hagersten) lock queue. CLH locks are normally used for
     * spinlocks.  We instead use them for blocking synchronizers, but
     * use the same basic tactic of holding some of the control
     * information about a thread in the predecessor of its node.  A
     * "status" field in each node keeps track of whether a thread
     * should block.  A node is signalled when its predecessor
     * releases.  Each node of the queue otherwise serves as a
     * specific-notification-style monitor holding a single waiting
     * thread. The status field does NOT control whether threads are
     * granted locks etc though.  A thread may try to acquire if it is
     * first in the queue. But being first does not guarantee success;
     * it only gives the right to contend.  So the currently released
     * contender thread may need to rewait.
     *
     * <p>To enqueue into a CLH lock, you atomically splice it in as new
     * tail. To dequeue, you just set the head field.
     * <pre>
     *      +------+  prev +-----+       +-----+
     * head |      | <---- |     | <---- |     |  tail
     *      +------+       +-----+       +-----+
     * </pre>
     *
     * <p>Insertion into a CLH queue requires only a single atomic
     * operation on "tail", so there is a simple atomic point of
     * demarcation from unqueued to queued. Similarly, dequeuing
     * involves only updating the "head". However, it takes a bit
     * more work for nodes to determine who their successors are,
     * in part to deal with possible cancellation due to timeouts
     * and interrupts.
     *
     * <p>The "prev" links (not used in original CLH locks), are mainly
     * needed to handle cancellation. If a node is cancelled, its
     * successor is (normally) relinked to a non-cancelled
     * predecessor. For explanation of similar mechanics in the case
     * of spin locks, see the papers by Scott and Scherer at
     * http://www.cs.rochester.edu/u/scott/synchronization/
     *
     * <p>We also use "next" links to implement blocking mechanics.
     * The thread id for each node is kept in its own node, so a
     * predecessor signals the next node to wake up by traversing
     * next link to determine which thread it is.  Determination of
     * successor must avoid races with newly queued nodes to set
     * the "next" fields of their predecessors.  This is solved
     * when necessary by checking backwards from the atomically
     * updated "tail" when a node's successor appears to be null.
     * (Or, said differently, the next-links are an optimization
     * so that we don't usually need a backward scan.)
     *
     * <p>Cancellation introduces some conservatism to the basic
     * algorithms.  Since we must poll for cancellation of other
     * nodes, we can miss noticing whether a cancelled node is
     * ahead or behind us. This is dealt with by always unparking
     * successors upon cancellation, allowing them to stabilize on
     * a new predecessor, unless we can identify an uncancelled
     * predecessor who will carry this responsibility.
     *
     * <p>CLH queues need a dummy header node to get started. But
     * we don't create them on construction, because it would be wasted
     * effort if there is never contention. Instead, the node
     * is constructed and head and tail pointers are set upon first
     * contention.
     *
     * <p>Threads waiting on Conditions use the same nodes, but
     * use an additional link. Conditions only need to link nodes
     * in simple (non-concurrent) linked queues because they are
     * only accessed when exclusively held.  Upon await, a node is
     * inserted into a condition queue.  Upon signal, the node is
     * transferred to the main queue.  A special value of status
     * field is used to mark which queue a node is on.
     *
     * <p>Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill
     * Scherer and Michael Scott, along with members of JSR-166
     * expert group, for helpful ideas, discussions, and critiques
     * on the design of this class.
     */
    static final class 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;

        /** 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;

        /**
         * 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;

        /**
         * 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;
        }

        /**
         * 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;
        }
    }

  

2.2 同步队列

  AQS 通过内置的 FIFO (先进先出)同步队列来完成资源获取线程的排队工作。如果当前线程获取锁失败时,AQS 会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会 park 当前线程;当同步状态释放时,则会把节点中的线程唤醒,使其再次尝试获取同步状态。

  

2.2.1 队列结构

  同步队列由双向链表实现,AQS持有头尾指针(head/tail属性)来管理同步队列。

在这里插入图片描述

  节点的数据结构,即AQS的静态内部类Node,包括节点对应的线程、节点的等待状态等信息。

  

2.2.2 节点类
static final class Node {
	/**
	* 当前节点/线程的前驱节点
	*/
    volatile Node prev;

	/**
	* 当前节点/线程的后继节点
	*/
    volatile Node next;

	/**
	* 每一个节点对应一个线程
	*/
    volatile Thread thread;
    
    /**
    * 节点状态
    * 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;// 

	/**
	* 节点状态:此线程取消了争抢这个锁
	*/
    static final int CANCELLED =  1;// 

	/**
	* 节点状态:当前node的后继节点对应的线程需要被唤醒(表示后继节点的状态)
	*/
    static final int SIGNAL = -1;// 

	/**
	* 节点状态:当前节点进入等待队列中
	*/
    static final int CONDITION = -2;

	/**
	* 节点状态:表示下一次共享式同步状态获取将会无条件传播下去
	*/
    static final int PROPAGATE = -3;

	/**
	* 下一个等待节点/线程
	*/
    Node nextWaiter;

	/**
	* 共享模式
	*/
    static final Node SHARED = new Node();

	/**
	* 独占模式
	*/
    static final Node EXCLUSIVE = null; 
}

  

2.2.3 入队操作
/**
* Creates and enqueues node for current thread and given mode.
*  
*  1.线程抢锁失败后,封装成node加入队列
*  2.队列有tail,可直接入队
* 	2.1入队时,通过CAS将node置为tail。CAS操作失败,说明被其它线程抢先入队了,
* 	node需要通过enq()方法入队
*  3.队列没有tail,说明队列是空的,node通过enq()方法入队,
*  enq()会初始化head和tail
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
	// 线程抢锁失败后,封装成node加入队列
   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) { // 如果有tail,node加入队尾
       node.prev = pred;
       // 通过CAS将node置为tail。CAS操作失败,说明被其它线程抢先入队了,
       // node需要通过enq()方法入队
       if (compareAndSetTail(pred, node)) {
           pred.next = node;
           return node;
       }
   }
   enq(node); // 如果没有tail,node通过enq()方法入队
   return node;
}

/**
* Inserts node into queue, initializing if necessary. See picture above.
*  1.通过自旋的方式将node入队,只有node入队成功才返回,否则一直循环
*  2.如果队列为空,初始化head/tail,初始化之后再次循环到else分支,将node入队
*  3.node入队时,通过CAS将node置为tail。CAS操作失败,说明被其它线程抢先入队了,
*  自旋,直到成功
* 
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
    for (;;) { // 自旋:循环入列,直到成功
        Node t = tail;
        if (t == null) { // Must initialize
        	// 初始化head/tail,初始化之后再次循环到else分支,将node入队
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
        	// node入队
            node.prev = t;
            // 通过CAS将node置为tail。操作失败,说明被其它线程抢先入队了,
            // 自旋,直到成功
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

  

2.3 获取锁

  以独占锁为例详细讲解获取锁及排队等待的过程。

/**
* Acquires in exclusive mode, ignoring interrupts.  Implemented
* by invoking at least once {@link #tryAcquire},
* returning on success.  Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success.  This method can be used
* to implement method {@link Lock#lock}.
*  
*  1.当前线程通过tryAcquire()方法抢锁
*  2.线程抢到锁,tryAcquire()返回true,结束
*  3.线程没有抢到锁,addWaiter()方法将当前线程封装成node加入同步队列,
*  并将node交由acquireQueued()处理
*
* @param arg the acquire argument.  This value is conveyed to
*        {@link #tryAcquire} but is otherwise uninterpreted and
*        can represent anything you like.
*/
public final void acquire(int arg) {
   if (!tryAcquire(arg) &&  // 子类的抢锁操作,下文有解释
   		// 子类抢锁失败进入队列中,重点方法,下文详细讲解
       acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
       selfInterrupt();
}

/**
 * 需要子类实现的抢锁的方法
 * 目前可以理解为通过CAS修改state的值,成功即为抢到锁,返回true;否则返回false。
 * 之后重入锁ReentrantLock、读写锁ReentrantReadWriteLock中会详细讲解。
 */
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}

/**
* Creates and enqueues node for current thread and given mode.
*  
* 上文介绍过的入队操作,线程抢锁失败,将当前线程封装成node加入同步队列,
* 并返回node, Node.EXCLUSIVE-表示独占锁,先不用关注
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
   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;
}


/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
* 
*  重点方法:
*  1.只有head的后继节点能去抢锁,一旦抢到锁旧head节点从队列中删除,
*  next被置为新head节点;
*  2.如果node线程没有获取到锁,将node线程挂起;
*  3.锁释放时head节点的后继节点唤醒,唤醒之后继续for循环抢锁.
*  
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
final boolean acquireQueued(final Node node, int arg) {
   boolean failed = true;
   try {
       boolean interrupted = false;
       for (;;) { // 循环

			/*
             * 1.node的前置节点是head时,可以调用tryAcquire()尝试去获取锁,
             * 	 获取锁成功则将node置为head
             * 注意:只有head的后继节点能去抢锁,一旦抢到锁旧head节点从队列中
             *      删除,next被置为新head节点
             * 2.node线程没有获取到锁,继续执行下面另一个if的代码
             *  此时有两种情况:1)node不是head的后继节点,没有资格抢锁;
             *               2)node是head的后继节点但抢锁没成功
             */

           final Node p = node.predecessor();
           if (p == head && tryAcquire(arg)) {
               setHead(node);
               p.next = null; // help GC
               failed = false;
               return interrupted;
           }

			/*
             * shouldParkAfterFailedAcquire(p, node):通过前置节点pred
             *   的状态waitStatus 来判断是否可以将node节点线程挂起
             * 
             * parkAndCheckInterrupt():将当前线程挂起
             * 1.如果node前置节点p.waitStatus==Node.SIGNAL(-1),
             *   直接将当前线程挂起,等待唤醒。
             *   锁释放时会将head节点的后继节点唤醒,唤醒之后继续for循环抢锁。
             * 
             * 2.如果node前置节点p.waitStatus<=0但是不等于-1,
             *      1)shouldParkAfterFailedAcquire(p, node)会将
             * 		  p.waitStatus置为-1,并返回false;
             *      2)进入一下次for循环,先尝试抢锁,没获取到锁则又到这里,
             *        此时p.waitStatus==-1,就会挂起当前线程。
             * 
             *  3.如果node前置节点p.waitStatus>0,
             *      1)shouldParkAfterFailedAcquire(p, node)为node找一个
             *        waitStatus<=0的前置节点,并返回false;
             *      2)继续for循环
             */

           if (shouldParkAfterFailedAcquire(p, node) &&
               parkAndCheckInterrupt())
               interrupted = true;
       }
   } finally {
       if (failed)
           cancelAcquire(node);
   }
}


/**
* Checks and updates status for a node that failed to acquire.
* Returns true if thread should block. This is the main signal
* control in all acquire loops.  Requires that pred == node.prev.
*  
* 通过前置节点pred的状态waitStatus 来判断是否可以将node节点线程挂起
* pred.waitStatus==Node.SIGNAL(-1)时,
* 	返回true表示可以挂起node线程;
* 	否则返回false
*
* @param pred node's predecessor holding status node的前置节点
* @param node the node 当前线程节点
* @return {@code true} if thread should block
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
   int ws = pred.waitStatus;
   if (ws == Node.SIGNAL)
       /*
        * This node has already set status asking a release
        * to signal it, so it can safely park.
        */
       return true;
   if (ws > 0) {
       /*
        * Predecessor was cancelled. Skip over predecessors and
        * indicate retry.
        *  
        * waitStatus>0 ,表示节点取消了排队
        * 这里检测一下,将不需要排队的线程从队列中删除
        * 	(因为同步队列中保存的是等锁的线程)
        * 为node找一个waitStatus<=0的前置节点pred 
        * 
        */
       do {
           node.prev = pred = pred.prev;
       } while (pred.waitStatus > 0);
       pred.next = node;
   } else {
       /*
        * waitStatus must be 0 or PROPAGATE.  Indicate that we
        * need a signal, but don't park yet.  Caller will need to
        * retry to make sure it cannot acquire before parking.
        * 
        * 此时pred.waitStatus<=0但是不等于-1,
        * 那么将pred.waitStatus置为Node.SIGNAL(-1) 
        * 
        */
       compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
   }
   return false;
}

/**
 * 将当前线程挂起
 * LockSupport.park()挂起当前线程;LockSupport.unpark(thread)唤醒线程thread
 */
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);// 将当前线程挂起
    return Thread.interrupted();
}

在这里插入图片描述
  

2.4 释放锁
/**
 * 释放锁之后,唤醒head的后继节点next。
 * 回顾上文讲的acquireQueued()方法,next节点会进入for循环的下一次循环去抢锁
 */
public final boolean release(int arg) {
    if (tryRelease(arg)) {// 子类实现的释放锁的方法,下文有讲解
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);// 唤醒node节点(也就是head)的后继节点,
        return true;
    }
    return false;
}

/**
 * 需要子类实现的释放锁的方法,对应于tryAcquire()
 * 目前可以理解为将state的值置为0。
 * 之后重入锁ReentrantLock、读写锁ReentrantReadWriteLock中会详细讲解。
 */
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}

/**
 * 唤醒node节点(也就是head)的后继节点
 */
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    Node s = node.next;// 正常情况,s就是head.next节点
    /*
     * 有可能head.next取消了等待(waitStatus==1)
     * 那么就从队尾往前找,找到waitStatus<=0的所有节点中排在最前面的去唤醒
     */
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);// 唤醒s节点的线程去抢锁
}

  

2.5 回顾整个历程

整个过程如下:

  1. 线程1来获取锁,此时没有竞争,直接获取到锁。AQS队列为空;
  2. 线程2来获取锁,因为线程1占用锁,线程2需要做两件事:
    2.1 线程2构造成Node到AQS的同步队列中排队。此时初始化同步队列;
    2.2 线程2阻塞,等待被唤醒之后再去抢锁。
  3. 线程3来获取锁,锁被占用,同样做两件事:排队并阻塞。此时的同步队列结构;
  4. 线程1执行完同步代码之后释放锁,唤醒head的后继节点(线程2),线程2获取锁,并把线程2对应的Node置为head;
  5. 线程2执行完同步代码之后释放锁,唤醒head的后继节点(线程3),线程3获取锁,并把线程3对应的Node置为head;
  6. 线程3执行完同步代码之后释放锁,同步队列中head之后没有节点了,将head置为null即可

在这里插入图片描述

  

3. 总结

  AQS结构:锁状态state、当前只有锁的线程exclusiveOwnerThread以及双向链表实现的同步队列。

  AQS使用模板方法设计模式,子类必须重写AQS获取锁tryAcquire()和释放锁tryRelease()的方法,一般是对state和exclusiveOwnerThread的操作。

获取锁acquire()过程:

  1. 子类调用tryAcquire()尝试获取锁,如果获取锁成功,完成;
  2. 如果获取锁失败,当前线程会封装成Node节点插入同步队列中,并且将当前线程park()阻塞,等待被唤醒之后再抢锁。

释放锁的过程:

  1. 当前线程调用子类的tryRelease()方法释放锁,释放锁成功后,会unpark(thread)唤醒head的后继节点,让其再去抢锁。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值