AQS源码分析

      AbstractQueuedSynchronized(AQS)是一个抽象的队列同步器,它本身是一个抽象类,提供一个FIFO的等待队列来存放阻塞资源和一个state状态来保存锁的信息。在实际的使用中,比如ReentrantLock和ReentrantReadWirteLock中,它们本身都有一个Syn类来继承AQS并做一些状态操作。

      锁的使用过程大概如下,在实际使用锁的过程中,比如我们使用一个lock()函数,同步类可以帮助我们通过tryAcquire来获取锁,如果可以获取则state+1,如果不可以则新建节点放入同步队列中,然后把这个线程阻塞。当我们使用一个realease()函数时,如果线程是持有锁的线程则状态减一,如果状态为0则唤醒后继节点。这里强调一下lock的作用,其实就是用来分配状态和阻塞节点,而unlock就是释放状态和唤醒节点

   AQS中代码结构如下,Node代表的是我们所说的队列中的节点。ConditionObject中放的是有关Condition相关的实现方法。和Lock(unLock)相关的函数包括两种,独占锁函数和共享锁函数,典型的就是ReentrantLock和ReentrantReadWirteLock。

Node

        /**标记一个节点是共享节点 */
        static final Node SHARED = new Node();
        /** 表示一个节点是独占节点 */
        static final Node EXCLUSIVE = null;

        /** 等待状态是要删除 */
        static final int CANCELLED =  1;
        /** 唤醒后继节点 */
        static final int SIGNAL    = -1;
        /**等待条件 */
        static final int CONDITION = -2;
        /**
         * 下一个获得的共享无条件传播
         */
        static final int PROPAGATE = -3;

        /**
         * 等待状态,前面四个,还有一个值是0,表示不是上面的四个。
         */
        volatile int waitStatus;

        /**
         *前驱节点
         */
        volatile Node prev;

        /**
         *后集结点
         */
        volatile Node next;

        /**
         * 节点中放的是线程
         */
        volatile Thread thread;   
      

   

Condition

     Condition在AQS中具体实现了,在其他的同步类中我们可以直接使用它,通过设置一个条件,在合适的时候通过调用await使一个线程沉睡并释放锁,当其他线程调用singal方法时会唤醒那个线程。我们通常可以将condition视为一个多线程之间通信的工具。

     Condition本身也维护一个等待条件队列,整个await过程如下:

      首先将该线程对应的节点放进条件等待队列中去,然后释放线程持有的全部锁,判断节点是否还在AQS的队列中(这里没有被singal前是没有被在队列中的),因此这里这个线程是会被挂起的。当后面signal方法被执行时,这里在条件队列中的线程还是会重新放在AQS的队列中去。

 /**
         * Implements interruptible condition wait.
         * <ol>
         * <li> If current thread is interrupted, throw InterruptedException.
         * <li> Save lock state returned by {@link #getState}.
         * <li> Invoke {@link #release} with saved state as argument,
         *      throwing IllegalMonitorStateException if it fails.
         * <li> Block until signalled or interrupted.
         * <li> Reacquire by invoking specialized version of
         *      {@link #acquire} with saved state as argument.
         * <li> If interrupted while blocked in step 4, throw InterruptedException.
         * </ol>
         */
        public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();//将节点放到condition队列中去
            int savedState = fullyRelease(node);//释放所有资源返回资源数目
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {//不在AQS队列中,阻塞
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)//获取自己之前所有的资源
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

    signal方法用于唤醒在条件队列中头结点的线程,如果条件队列中的头结点不为空,那么就执行doSingal()函数。维护条件队列中的节点,将头结点放入AQS尾部,等待自己释放锁后,原来的await线程就可以获取资源执行了。

 /**
         * Moves the longest-waiting thread, if one exists, from the
         * wait queue for this condition to the wait queue for the
         * owning lock.
         *
         * @throws IllegalMonitorStateException if {@link #isHeldExclusively}
         *         returns {@code false}
         */
        public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }


         * Removes and transfers nodes until hit non-cancelled one or
         * null. Split out from signal in part to encourage compilers
         * to inline the case of no waiters.
         * @param first (non-null) the first node on condition queue
         */
        private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&//将头结点放到AQS
                     (first = firstWaiter) != null);
        } 
 /**
     * Transfers a node from a condition queue onto sync queue.
     * Returns true if successful.
     * @param node the node
     * @return true if successfully transferred (else the node was
     * cancelled before signal)
     */
    final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);//把节点放进AQS的尾部
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))//唤醒节点,进行资源抢占
            LockSupport.unpark(node.thread);//这里其实状态并不正常
        return true;  }

acquire()

    独占锁模式下可以通过这个函数来获取锁,获取独占锁的过程是这样的。首先使用tryAcquire函数,这个函数在AQS中只是一个接口,具体实现在其实现类中实现,可以参照前面reentrantLock中的实现,主要作用是用来获取资源,如果获取到资源直接返回。否则为该线程新设置一个节点然后放到队列后面,然后在队列中找到头结点后面的节点,然后判断此过程中是否需要阻塞该线程。过程如下:

  

具体过程可以看reentrantLock分析。

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

独占模式下释放资源,执行tryRealease(1)尝试释放锁,如果线程不一致则抛出异常,否则状态为0 则返回true,唤醒后继节点。

public final boolean release(int arg) {  
    if (tryRelease(arg)) {//尝试释放锁,其实就是状态减一。  
        Node h = head;  
        if (h != null && h.waitStatus != 0)  
            unparkSuccessor(h);//成功则唤醒后继节点  
        return true;  
    }  
    return false;  
}  

acquiredShare()

   共享锁的情况下同样是要维护一个队列和一个状态,但是在这里把state分成两个部分,高16为用于表示读锁的状态,低16为用来表示写锁的状态。

共享模式下获得锁的过程比较复杂,它的过程简单描述如下:

 /**
     * 忽略中断,以共享的模式获取锁  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);//失败则进入等待队列一直到获取资源为止
    }

releaseShared()

共享模式下释放资源分为两步,首先尝试释放共享资源,如果成功则进行后续的唤醒操作。否则的话直接失败。

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

   整体来讲,AQS最重要的函数以及流程就是上面这几个,其他的比如tryLock或者是带时间的tryLock都是类似的。这里就不展开赘述了。

参考资料:

http://ifeve.com/understand-condition/

https://blog.csdn.net/pb_yan/article/details/80572194

http://ifeve.com/jdk1-8-abstractqueuedsynchronizer

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

https://blog.csdn.net/HEYUTAO007/article/details/49889849

猜你喜欢

转载自blog.csdn.net/pb_yan/article/details/80599728