Simple to understand the source of AQS

concept

AQS stands AbstractQueuedSynchronizer.

AQS is a contract basis and components used to implement a variety of locks, various synchronization components. It contains state variables, locking thread, waiting for the core components of concurrent queues.

ReentrantLock, Semaphore, CountDownLatch, FutrueTask, these are based AQS built.

The AQS is to achieve read / write and CAS volatile variables (ie compareAndSet () method) based.

volatile ensure visibility of concurrency may also prohibit instruction reordering. CAS, used to manage concurrent access to shared data.

AQS and the relationship ReentrantLock

In the ReentrantLock, state represents the locked state. In the initial state, the state value is 0.

In addition, the AQS inside there is a key variable for the current record which thread is locked, initialized state, this variable is null.

A thread ran to call the lock ReentrantLock () method attempts to lock, the locking process, is to use CAS direct operation state value from 0 to 1.
If you do not add people had before the lock, then the value of state is certainly 0, then A thread can successfully lock.
Once locked thread 1 is successful, you can set the current thread is locked themselves.

The main flow

Important variables

  • state
    state variables.
/**
 * The synchronization state.
 */
private volatile int state;

Node Node queue waiting

"CLH" (Craig, Landin, and Hagersten) lock queue。

CLH lock queue is usually used to handle concurrent situation, it is accomplished via a bidirectional synchronization status queue (FIFO). Each thread is encapsulated into a synchronization queue node Node.

Each Node node queue save the state of the current thread synchronization, wait state, predecessor and successor nodes and so on.

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;

        // 线程的等待状态 表示线程已经被取消
        static final int CANCELLED =  1;
        // 线程的等待状态 表示后继线程需要被唤醒
        static final int SIGNAL    = -1;
        // 线程的等待状态 表示线程在Condtion上
        static final int CONDITION = -2;
        // 表示下一个acquireShared需要无条件的传播
        static final int PROPAGATE = -3;

        /**
         *   等待状态有以下几种:
         *   
         *   SIGNAL:     当前节点的后继节点处于等待状态时,如果当前节点的同步状态被释放或者取消,
         *               必须唤起它的后继节点
         *         
         *   CANCELLED:  一个节点由于超时或者中断需要在CLH队列中取消等待状态,被取消的节点不会再次等待
         *               
         *   CONDITION:  当前节点在等待队列中,只有当节点的状态设为0的时候该节点才会被转移到同步队列
         *               
         *   PROPAGATE:  下一次的共享模式同步状态的获取将会无条件的传播
 
         *   waitStatus的初始值时0,使用CAS来修改节点的状态
         */
        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;
        }
    }

acquire()

Thread gets state in exclusive mode.

After subclass inherits AQS, often used acquire (), release ().

acquire () to obtain the status, maintenance status.

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

release()

Thread releases the state in exclusive mode.

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

unparkSuccessor()

Wake successor node of Node.

    /**
     * Wakes up node's successor, if one exists.
     *
     * @param node the node
     */
    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        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);
    }

compareAndSetState()

Set state variables CAS. When the state is a specified parameter expect, it will modify the value specified for the other.

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

ConditionObject

ConditionObject other inner class implements the Condition interface, and which achieved await (), signal (), signalALL () method and the like.
ConditionObject mainly for concurrent implementation provides synchronous waiting to be notified of programming, can suspend a thread waiting time does not meet certain conditions. Until some condition is met when the wake-up thread.

References:

https://juejin.im/post/5c07e59cf265da617464a09c (vernacular talk about the understanding of the AQS)

http://ifeve.com/introduce-abstractqueuedsynchronizer/

https://blog.csdn.net/qq_30572275/article/details/80297047

Guess you like

Origin www.cnblogs.com/expiator/p/12052125.html