12.AQS源码分析

本文详细介绍了Java并发库中的AbstractQueuedSynchronizer(AQS)的核心概念,包括其独占与共享资源的管理模式、同步队列的节点结构以及获取和释放锁的流程。AQS通过内部的FIFO队列管理线程的等待,并通过CAS操作保证线程安全。文章还剖析了AQS的源码,展示了如何通过自定义同步器实现锁和其他同步组件。
摘要由CSDN通过智能技术生成

目录

1)AQS 概要

1.AQS 原理

2.AQS 资源共享方式

2)AQS 源码分析

2.1 AQS 类结构

2.1.1 属性

2.1.2 方法

2.1.3 内部类

2.2 同步队列

2.2.1 队列结构

2.2.2 节点类

2.2.3 入队操作

2.3 获取锁

2.4 释放锁

2.5 回顾整个历程

3)总结


1)AQS 概要

1.AQS 原理

AbstractQueuedSynchronizer 类如其名,抽象的队列式的同步器.

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

在这里插入图片描述

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的后继节点,让其再去抢锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值