AQS原理

AbstractQueuedSynchronizer(AQS)实现原理

AQS中包含两种锁

  1. 独占锁,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁
  2. 共享锁,则允许多个线程同时获取锁,并发访问 共享资源(只能读),但是只能有一个线程进行写操作,如:ReadWriteLock
    锁队列的实现都是基于CLH的一种变体,在其队列节点的前驱上自旋

队列节点定义

   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).
         */
        //属于初始化0,用于条件阻塞
        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;
        }
    }

1.独占锁实现

acquire获取独占锁

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

    //子类实现,尝试获取锁,get lock -> true or false
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

   //创建个Node  A 入队
   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;
        //前驱节点为null,注意return
        if (pred != null) {
            node.prev = pred;
            //tail指向创建的Node
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //入队
        enq(node);
        return node;
    }

   //入队
   private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
---------------------------------------------------
acquireQueued(进行前驱节点自旋)
---------------------------------------------------
  final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

挂起未获得锁的线程,并检测挂起线程是否中断

   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.
             */
            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.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

    private final boolean parkAndCheckInterrupt() {
        //挂起此线程
        LockSupport.park(this);
        //此代码必须等到线程被唤醒后才会执行
        return Thread.interrupted();
    }

上述代码分析可得:
1. thread1 执行lock锁定,调用acquire方法获取锁,tryAcquire返回true,由acquire实现可以看出,!tryAcquire(arg)为false,线程获取锁成功,此时不释放锁
2. thread2 执行lock锁定,tryAcquire返回false,开始执行acquireQueued方法,此时开始初始化Node节点,node队列如下: Thread2插入的节点是B,A是队列初始化插入的头节点,且线程在其前驱节点A上自旋

|       |   | Node  |    | Node |
|thread1|---|  A    |--->|  B   |
|       |   |       |<---|      |
|       |   | head  |    | tail |   
            |thread2|    |      |

可以看出thrad2绑定到nodeB上,但是其自旋在前驱节点A上,LockSupport.park挂起此线程
3.thread3 执行lock锁定,
Thread3插入的是节点C,自旋在其前驱节点B上。

|       |   | Node  |    | Node  |    | Node |   
|thread1|---|  A    |--->|  B    |--->|  C   |
|       |   |       |<---|       |<-->|      |
|       |   | head  |    |       |    |      |  
            |thread2|    |thread3|    |      |

thread3绑定在节点C上,但是其自旋在前驱节点B上,LockSupport.park挂起此线程。
由此可以看出,前一个锁中断或者释放锁,只需要唤醒node下一个引用的节点所关联的线程就可以获取到锁

release释放独占锁

  public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
  //唤醒下个被挂起的线程  
  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);
    }

释放锁的过程:

|       |   | Node |    | Node  |    | Node |   
|thread1|---|  A   |--->|  B    |--->|  C   |
|       |   |      |<---|       |<-->|      |
|       |   | head |    |       |    |      |  
            |      |    |thread3|    |      |

thread1内执行release操作,唤醒thread2,thread2执行realse操作唤醒thread3,
实现独占锁

2.共享锁实现

acquireShared(获取共享锁)


    public final void acquireShared(int arg) {
        //小于0,线程进行挂起
        if (tryAcquireShared(arg) < 0)
            //添加到锁队列
            doAcquireShared(arg);
    }

    //供子类实现
    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

    //挂起线程 
   private void doAcquireShared(int arg) {
        //添加到锁队列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                //获取前驱结点
                final Node p = node.predecessor();
                if (p == head) {
                   //尝试获取许可
                    int r = tryAcquireShared(arg);
                    //获取许可成功
                    if (r >= 0) {
                        //设置队列头,传递许可到下个节点
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //获取许可失败线程进行挂起并在唤醒后检测挂起线程是否中断
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
setHeadAndPropagate 共享锁传递许可
 private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            //下个节点不为空并且是共享的
            if (s == null || s.isShared())
                //释放
                doReleaseShared();
        }
    }


  //释放锁
  private void doReleaseShared() {

        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;  //-1
                if (ws == Node.SIGNAL) { //true
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    //唤醒挂起的线程
                    unparkSuccessor(h);
                }
                //线程还在处在准备阶段
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

releaseShared(释放共享锁)

  public final boolean releaseShared(int arg) {
       // >=1 = true  达到释放锁的阙值
        if (tryReleaseShared(arg)) {
            //见上
            doReleaseShared();
            return true;
        }
        return false;
    }

请求共享锁后CLSH队列跟独占锁的一样,不同的是绑定的锁类型标识不一样
如下:

|       |   | Node  |    | Node  |    | Node |   
|thread1|---|  A    |--->|  B    |--->|  C   |
|       |   |       |<---|       |<-->|      |
|       |   | head  |    |       |    |      |  
            |thread2|    |thread3|    |      |

达到条件后进行释放操作:

执行releaseShared操作,并且达到阙值,执行doReleaseShared,此时会唤醒thread1,thread1执行setHeadAndPropagate函数唤醒下一个线程thread2,依次往下传递唤醒。
共享锁是在唤醒下一个线程后return,才开始执行锁定的代码

AQS实现的共享锁是公平锁

3.Condition实现

基于链表队列实现的wait/notify,对应方法await/signal 执行await时入队并park此线程,执行signal时唤醒一个线程,执行signal时唤醒此condition的所有阻塞线程

**一个lock可以对应多个conndition,每个conndition都有其自己的阻塞队列,
而signal和signalAll只会唤醒自己阻塞队列里绑定的线程。**

可以实现多线程间的单对单,单对多通信

signal唤醒的顺序是跟线程的入队顺序一致,FIFO

猜你喜欢

转载自blog.csdn.net/rambokitty/article/details/80492780