JUC的AQS的第三部分:AQS(AbstractQueuedSynchronizer)代码详解

概述

这个类是抽象类,一些方法需要子类来进行实现。

java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquire
java.util.concurrent.locks.AbstractQueuedSynchronizer#tryRelease
java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquireShared
java.util.concurrent.locks.AbstractQueuedSynchronizer#tryReleaseShared
java.util.concurrent.locks.AbstractQueuedSynchronizer#isHeldExclusively

核心的方法是aquire。此方法是独占模式下线程获取共享资源的顶层入口。如果获取到资源,线程直接返回,否则进入等待队列,直到获取到资源为止。

代码分析

AbstractQueuedSynchronizer在JUC的locks包中。

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {

	//空的
	protected AbstractQueuedSynchronizer() { }

我们首先来看node对象
在这里插入图片描述
可以看到node其实就是连标上的一个节点。而且存储了这个节点对应的线程。我们可以通过这个线程的应用使用sun.misc.Unsafe#unpark对其进行unpark。
节点大致有如下状态

  • CANCELLED(1):表示当前结点已取消调度。当timeout或被中断(响应中断的情况下),会触发变更为此状态,进入该状态后的结点将不会再变化。

  • SIGNAL(-1):表示后继结点在等待当前结点唤醒。后继结点入队时,会将前继结点的状态更新为SIGNAL。

_ CONDITION(-2):表示结点等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁。

  • PROPAGATE(-3):共享模式下,前继结点不仅会唤醒其后继结点,同时也可能会唤醒后继的后继结点。

0:新结点入队时的默认状态。

内部存储着等待队列的头尾

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

这里写图片描述

在这里插入图片描述

acquire()操作流程
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            //先加入队列,然后进行阻断
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            //阻断被唤醒后首先响应这段时间收到的中断
            selfInterrupt();
    }
  1. tryAcquire()尝试直接去获取资源,如果成功则直接返回(这里体现了非公平锁,每个线程获取锁时会尝试直接抢占加塞一次,而CLH队列中可能还有别的线程在等待);
  2. addWaiter()将该线程加入等待队列的尾部,并标记为独占模式;
  3. acquireQueued()使线程阻塞在等待队列中获取资源,一直获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
  4. 如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。
    注意,这里要传入一个整数值,用于子类实现者在实现tryAquire中使用。这里没有明确含义。
    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;
                }
                //shouldParkAfterFailedAcquire将检查或将前序节点设置为signal状态
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

	//线程在被park时不会响应中断。在唤醒后将返回其中断状态
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
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);
    }
ConditionObject

这个内部类不一定会在核心链路上使用的。

        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;

作为自身内部链表【被称为等待队列】首尾节点。

在这里插入图片描述
一个AQS对象内部可能有多个condition对象,对应多个等待队列。

调用condition的signal方法时,将会把等待队列的首节点移到等待队列的尾部,然后唤醒该节点。
被唤醒,并不代表就会从await方法返回,也不代表该节点的线程能获取到锁,它一样需要加入到锁的竞争acquireQueued方法中去,只有成功竞争到锁,才能从await方法返回
在这里插入图片描述

        public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }

        private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (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);
        int ws = p.waitStatus;
        //ws>0表示取消调度,确认没有取消调度 
        //将前序节点状态设置为signal状态
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            //让这个线程继续运行
            LockSupport.unpark(node.thread);
        return true;
    }

上面的signal上线程继续执行。那么线程究竟被wait在哪里的了呢?我们还是需要看wait的实现

调用condition的await方法,将会使当前线程进入等待队列并释放锁(先加入等待队列再释放锁),同时线程状态转为等待状态。
从同步队列和阻塞队列的角度看,调用await方法时,相当于同步队列的首节点移到condition的等待队列中
在这里插入图片描述

        /**
         * 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();
            //将该线程加到condition等待队列的队尾    
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                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);
        }

可见,线程被唤醒后一样会进入到acquireQueued的逻辑去争取同步队列的锁。

再看ReentrantLock

无论是

ReentrantLock中的公平锁代码

        final void lock() {
            acquire(1);
        }


        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();//等于0表示目前尚无线程获取锁。
            if (c == 0) {//表示当前尚无线程获取锁
                if (!hasQueuedPredecessors() &&//判断队列里是有有前序节点等待
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //判断获取锁的是不是当前线程
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                //整形溢出报错。
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

我们再来看看非公平锁的代码



        final void lock() {
        	//如果没有则直接抢占
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值