AbstractQueuedSynchronizer源码分析(1)

1 介绍

在分析 Java 并发包 java.util.concurrent 源码的时候,少不了需要了解 AbstractQueuedSynchronizer(以下简写AQS)这个抽象类,因为它是 Java 并发包的基础工具类,是实现 ReentrantLock、CountDownLatch、Semaphore、FutureTask 等类的基础。
Google 一下 AbstractQueuedSynchronizer,我们可以找到很多关于 AQS 的介绍,但是很多都没有介绍清楚,因为大部分文章没有把其中的一些关键的细节说清楚。
本文将从 ReentrantLock 的公平锁源码出发,分析下 AbstractQueuedSynchronizer 这个类是怎么工作的,希望能给大家提供一些简单的帮助。

2 AQS 结构和内部类node介绍

2.1 AQS属性

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

    /**
     * 锁的状态
     *  这个是最重要的,不过也是最简单的,代表当前锁的状态,0代表没有被占用,大于0代表有线程持有当前锁
     *  之所以说大于0,而不是等于1,是因为锁可以重入嘛,每次重入都加上1
     */
    private volatile int state;
	/**
	 * The current owner of exclusive mode synchronization.
     * 这个是继承来的。相当于就是标记占用锁的线程。
     * 也就是代表当前持有独占锁的线程,举个最重要的使用例子,因为锁可以重入
	 * reentrantLock.lock()可以嵌套调用多次,所以每次用这个来判断当前线程是否已经拥有了锁
	 * if (currentThread == getExclusiveOwnerThread()) {state++}
     */
    private transient Thread exclusiveOwnerThread;

还是比较简单的,毕竟也就四个属性。重点是里面的node节点(每个线程对应一个节点)。

AbstractQueuedSynchronizer的阻塞队列示意如下所示,但是要注意了,之后分析过程中的阻塞队列不包含 head,不包含 head,不包含 head(重要的事情说仨遍)并且head节点代表就是获取到锁的线程节点
阻塞队列示意图
阻塞队列结构就是由node节点组成的双向链表。一个线程包装成一个node节点。

2.2 node节点结构

前面说了每个节点中独有包装一个线程。以及上下节点的关联。来组成双向链表来形成一个阻塞队列。这里要注意后面还会有一个条件队列也是由node节点组成,但是他是一个单向链表,node节点结构如下所示

static final class Node {
        /**     模式标记,分为共享与独占    */
        // 共享
        static final Node SHARED = new Node();
        // 私有 独占
        static final Node EXCLUSIVE = null;

        /**
         * waitStatus的值  看下面的翻译有说明
         * CANCELLED =1:代码此线程取消了争抢这个锁
         * SIGNAL=-1:表示当前node的后继节点对应的线程需要被唤醒  这个状态是当有后继节点加进来的去设置的默认是0
         * 也就是说如果当前节点是尾节点的话  一般他的状态就是0
         * CONDITION =-2:表示当前node就是条件队列中这个后面会讲。暂时用不到先不管
         * PROPAGATE =-3:同样的不分析,略过吧
         * */
        static final int CANCELLED =  1;
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;
        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.
         */
        // 上一个节点  和next节点组成双向链表的阻塞队列
        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.
         */
        // 下一个节点   和prev节点组成双向链表的阻塞队列
        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;
        }
    }

Node 的属性结构也不难,就四个属性thread + waitStatus + prev + next,但是大家心里一定要有这个概念。同时它的几种状态大家要清楚。

因为后面会多次用到,心里要时刻记着它们,心里想着上面阻塞队列的结构图就可以了。下面,我们开始说 ReentrantLock 的公平锁。

2.3 ReentrantLock的使用方式

public class OrderService {
    // 这里是创建      
    // 注意构造方法可以传一个布尔的参数。它是用来创建不同的锁用的。默认是创建非公平锁。
    // 当传入true时就是创建一个公平锁。
    // 公平锁和非公平锁后面会说明的  这里暂时先不管
    private ReentrantLock reentrantLock = new ReentrantLock(true);

    public void placeOrder() {
        // 比如我们要在同一时间,只允许一个线程创建订单的话
        reentrantLock.lock();
        // 通常,lock 之后紧跟着 try 语句。并且reentrantLock.lock();不要放在try里面。那样有可能会无辜释放锁。
        // 因为如果lock()方法中没有获取到锁,但是却发生异常。finally就会释放。可能会出现问题
        try {
            // 这块代码只能有一个线程进来(获取到锁的线程),
            // 其他的线程在lock()方法上阻塞,等待获取到锁,再进来
            // 执行代码...
        } finally {
            // 释放锁
            reentrantLock.unlock();
        }
    }
}

2.3.1 ReentrantLock内部Sync实现

到这不熟悉源码的同学可能会觉得创建ReentrantLock和AQS有什么联系吗?并没有创建AQS呀。其实在ReentrantLock 的内部用了内部类 Sync 来管理锁,而Sync又是继承自AQS,所以真正的获取锁和释放锁是由 Sync 的实现类来控制的。

abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         *
         * 尝试获取锁
         *
         * 这个是非公平的方式去获取锁
         *
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // compareAndSetState这个判断是否获取到锁
                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;
        }

        // 这释放锁的操作  这里看传入的参数   可以直接完全释放
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
    }

而前面说的公平锁和非公平锁又是继承自Sync来实现获取和释放锁的

2.3.2 ReentrantLock内部公平锁(FairSync )实现

公平锁

/**
     * 公平锁的实现
     * Sync object for fair locks
     *
     *
     * 公平锁和非公平锁的区别就是  这个就是有没有判断队列是否有等待的线程
     *
     * 公平锁是先去查看对列中是否有等待的线程,有的话会遵循链表顺序去执行,反之直接执行(因为没有等待的线程直接执行)
     * 非公平是直接去尝试获取锁  获取去到就执行  没获取到也加入队列
     */
    static final class FairSync extends ReentrantLock.Sync {
        private static final long serialVersionUID = -3000897897090466540L;

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

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         *
         * 尝试获取锁
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // hasQueuedPredecessors这个是判断队列中是否还有其他的线程在等待
                // compareAndSetState这个判断是否获取到锁


                // 和非公平锁的不同点    (非公平锁直接尝试获取锁,公平锁先查看阻塞队列是否有阻塞的线程)
                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;
        }
    }

2.3.3 ReentrantLock内部非公平锁(NonfairSync)实现

非公平锁

/**
     * 非公平锁
     * Sync object for non-fair locks
     *
     * 公平锁和非公平锁的区别就是  这个就是有没有判断队列是否有等待的线程
     *
     * 公平锁是先去查看对列中是否有等待的线程,有的话会遵循链表顺序去执行,反之直接执行(因为没有等待的线程直接执行)
     * 非公平是直接去尝试获取锁  获取去到就执行  没获取到也加入队列
     */
    static final class NonfairSync extends ReentrantLock.Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

3 获取锁和释放锁

3.1 线程获取锁

这里以公平锁获取为例。ps 为了方便阅读有些方法我放在下面一起了 (比如acquire(int arg)其实是来自父类AQS中的)

/**
     * 公平锁的实现
     * Sync object for fair locks
     *
     *
     * 公平锁和非公平锁的区别就是  这个就是有没有判断队列是否有等待的线程
     *
     * 公平锁是先去查看对列中是否有等待的线程,有的话会遵循链表顺序去执行,
     * 反之直接执行(因为没有等待的线程直接执行)
     * 非公平是直接去尝试获取锁  获取去到就执行  没获取到也加入队列
     */
    static final class FairSync extends ReentrantLock.Sync {
        private static final long serialVersionUID = -3000897897090466540L;

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

		/**
		* 这个方法是继承自AQS中的方法
		*/
		public final void acquire(int arg) {
	        // tryAcquire(arg)是尝试获取锁  获取成功返回true 反之false
	        // 获取成功就一直往下执行 我们的业务代码  不会再执行acquireQueued()方法了
	        // 获取失败后,执行addWaiter和acquireQueued方法将当前线程封装成node对象
	        // 并放入等待链表中等待获取锁 也就是进到阻塞队列的最后
	        if (!tryAcquire(arg) &&
	                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
	            selfInterrupt();
		}

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         *
         * 尝试获取锁
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // hasQueuedPredecessors这个是判断队列中是否还有其他的线程在等待
                // compareAndSetState这个判断是否获取到锁


                // 和非公平锁的不同点    (非公平锁直接尝试获取锁,公平锁先查看阻塞队列是否有阻塞的线程)
                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;
        }
    }

	// 和enq意思差不多  enq就是有一个初始化的过程和不成功就是循环执行(成功为止)
    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;
            // 将node设置成尾节点
            if (compareAndSetTail(pred, node)) {
                // 进到这里说明设置成功,当前node==tail, 将自己与之前的队尾相连,
                // 上面已经有 node.prev = pred
                // 加上下面这句,也就实现了和之前的尾节点双向连接了
                pred.next = node;
                return node;
            }
        }
        // 仔细看看上面的代码,如果会到这里,
        // 说明 pred==null(队列是空的) 或者 CAS失败(有线程在竞争入队compareAndSetTail执行失败)
        enq(node);
        return node;
    }

/**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * @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 (;;) {
                // 获取当前节点的前驱节点
                final Node p = node.predecessor();
                // 如果前一个节点是头结点   并且尝试获取锁成功
                if (p == head && tryAcquire(arg)) {
                    // 将当前节点设置成头结点
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 到这里,说明上面没有执行成功,或者当前node不是队头,
                // 要么就是tryAcquire(arg)没有抢赢别人,继续往下看
                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.
     *
     * 说明当前线程没有抢到锁,是否需要挂起当前线程?
     * 
     * 注意这里是再循环体里面
     * 注意这里是再循环体里面
     * 注意这里是再循环体里面
     *
     * @param pred node's predecessor holding status
     * @param node the node
     * @return {@code true} if thread should block
     * 注意这里是再循环体里面
     * 第一次进来可能会执行else里面的代码,将上一个节点的状态设置成-1
     * 第二次执行就会执行第一个if语句里面的。然后返回ture
     */
    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
             *
             *
             * 这里是跳过前驱节点状态 大于0的节点
             *
             * 往上遍历查找前驱节点
             * 前驱节点 waitStatus大于0 ,之前说过,大于0 说明前驱节点取消了排队。这里需要知道这点:
             * 进入阻塞队列排队的线程会被挂起,而唤醒的操作是由前驱节点完成的。
             * 所以下面这块代码说的是将当前节点的prev指向waitStatus<=0的节点,
             * 简单说,就是为了找个好爹,因为你还得依赖它来唤醒呢,如果前驱节点取消了排队,
             * 找前驱节点的前驱节点做爹,往前循环总能找到一个好爹的
             *
             */
            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.
             *
             * 仔细想想,如果进入到这个分支意味着什么
             * 前驱节点的waitStatus不等于-1和1,那也就是只可能是0,-2,-3
             * 在我们前面的源码中,都没有看到有设置waitStatus的,所以每个新的node入队时,waitStatu都是0
             * 用CAS将前驱节点的waitStatus设置为Node.SIGNAL(也就是-1)
             * 这里就是将赏你个节点的状态改为-1
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
    
    /**
     * Convenience method to park and then check if
     *
     * 挂起当前线程 并检查是否已经中断
     * 这里用了LockSupport.park(this)来挂起线程,然后就停在这里了,等待被唤醒=======
     * @return {@code true} if interrupted
     */
    private final boolean parkAndCheckInterrupt() {
        // 挂起线程等待被唤醒  执行到这线程就停在这里了  等待被唤醒
        LockSupport.park(this);
        // 检查是否已经中断
        return Thread.interrupted();
    }

到这获取锁也就差不多说明白了。获取到了就继续执行,内有获取到就挂起暂停等被唤醒。不明白的可以多看几遍

3.2 线程释放锁

最后介绍下唤醒的动作了

// 唤醒的代码还是比较简单的,你如果上面加锁的都看懂了,下面都不需要看就知道怎么回事了
// 解锁一般都是解锁一次   因为有重入的可能每次只解锁一次。特殊需求除外也可以传入具体的次数
public void unlock() {
    sync.release(1);
}

public final boolean release(int arg) {
    // 往后看吧
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

// 这释放锁的操作  这里看传入的参数   可以直接完全释放
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    //判断线程是不是同一个 不是则有问题了
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    // 是否完全释放锁
    boolean free = false;
    //其实就是重入的问题,如果c==0,也就是说没有嵌套锁了,可以释放了,否则还不能释放掉
    if (c == 0) {
        free = true;
        //设置占用锁的线程为空。也就是没有线程占用锁
        setExclusiveOwnerThread(null);
    }
    // 没有完全释放锁 需要将锁释放c次  这里是重入的设计
    setState(c);
    return free;
}

/**
 1. Wakes up node's successor, if one exists.
 2.  3. @param node the node
 */
// 唤醒后继节点
// 从上面调用处知道,参数node是head头结点
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;
    // 如果head节点当前waitStatus<0, 将其修改为0
    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.
     */
    // 下面的代码就是唤醒后继节点,但是有可能后继节点取消了等待(waitStatus==1)
    // 从队尾往前找,找到waitStatus<=0的所有节点中排在最前面的
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 从后往前找,仔细看代码,不必担心中间有节点取消(waitStatus==1)的情况
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        // 唤醒线程
        LockSupport.unpark(s.thread);
}

唤醒下一个节点的线程以后,被唤醒的线程将从以下继续往前走:

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this); // 刚刚线程被挂起在这里了
    return Thread.interrupted();
}

说到这不知道大家有没有注意在解锁的时候。只是唤醒了。并没有将头结点的上一个节点取消关联。也就是唤醒后没有将上一个获取到锁的节点剔除在双向链表中
其实是有的只是并没有放在解锁这边而是放在acquireQueued(final Node node, int arg)方法中。就是 // help GC这句前面的还有在setHead(node)中

/**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * @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 (;;) {
                // 获取当前节点的前驱节点
                final Node p = node.predecessor();
                // 如果前一个节点是头结点   并且尝试获取锁成功
                if (p == head && tryAcquire(arg)) {
                    // 将当前节点设置成头结点
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 到这里,说明上面没有执行成功,或者当前node不是队头,
                // 要么就是tryAcquire(arg)没有抢赢别人,继续往下看
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

4 总结

在并发环境下,加锁和解锁需要以下三个部件的协调:

  1. 锁状态。怎么知道锁是不是被占用被占用了几次(重入问题state等于就是占用几次),这个就是 state 的作用,0 代表没有线程占有锁,线程就可以去争抢这个锁,用 CAS 将 state 设为 1,如果 CAS 成功,说明抢到了锁,这样其他线程就抢不到了,如果锁重入的话,state进行+1 就可以,解锁就是减 1,直到 state 又变为 0,代表释放锁,所以 lock() 和 unlock() 必须要配对啊。然后唤醒等待队列中的第一个线程,让其来占有锁。
  2. 线程的阻塞和解除阻塞。AQS 中采用了 LockSupport.park(thread) 来挂起线程,用 unpark 来唤醒线程。
  3. 阻塞队列。因为争抢锁的线程可能很多,AQS 用的是一个 FIFO 的队列,就是一个链表,每个 node 都持有后继节点的引用。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值