AbstractQueuedSynchronizer(AQS)

165 阅读20分钟

一、AQS是什么

   1、同步器模板,基于模板模式开发

   2、提供 独占式(Exclusive) 和 共享式(Share) 两种同步方式。                            独占式:公平锁 -- 排队,先到先得 / 非公平锁 -- 直接抢                                共享式:同时获取

二、AQS数据结构

   1、AQS底层是一个CLH虚拟双向队列(FIFO,不存在队列实例,仅有节点之间的关联关系)。AQS通过将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配

   2、AQS使用一个Volatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,通过CAS完成对State值的修改

image.png image.png image.png image.png

三、代码 + 图文 对部分源码及工作流程的解析

  • 关于 state 状态字段
/**
 * AQS中维护了一个名为state的字段,意为同步状态,是由Volatile修饰的,用于展示当前临界资源的获锁情况
 */

private volatile int state;

protected final int getState() {
    return state;
}

protected final void setState(int newState) {
    state = newState;
}

/**
 * 如果当前状态值等于预期值,那么就使用给定的更新值原子性地设置同步状态。
 * CAS
 */
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

image.png

image.png

  • 关于自定义同步器
/**
 * 自定义同步器时需要重写下面几个AQS提供的模板方法:
 */

/**
 * 该线程是否正在独占资源。只有用到condition才需要去实现它。
 */
isHeldExclusively();

/**
 * 独占方式。尝试获取资源,成功则返回true,失败则返回false。
 */
tryAcquire(int)

/**
 * 独占方式。尝试释放资源,成功则返回true,失败则返回false。
 */
tryRelease(int);

/**
 * 共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
 */
tryAcquireShared(int);

/**
 * 共享方式。尝试释放资源,成功则返回true,失败则返回false。 
 */
tryReleaseShared(int);

/**
 * 默认情况下,每个方法都抛出 UnsupportedOperationException。 
 * 这些方法的实现必须是内部线程安全的,并且通常应该简短而不是阻塞。
 * AQS类中的其他方法都是final ,所以无法被其他类使用,只有这几个方法可以被其他类使用
 *
 *  为了方便看,AQS的源码贴在下方
 */
  • 关于AQS原理及流程。(以下结合 ReentrantLock 展示)

       1、同步器核心思想:

            - 被请求的共享资源处于空闲状态,则当前请求资源的工作线程为有效工作线程,并锁定共享资源         - 被请求的贡献资源被占用,则需要完整的线程阻塞、唤醒机制。(例如AQS利用CLH队列来实现)

        2、ReentrantLock中 公平锁、非公平锁 与 AQS 的关系 (加锁和释放锁)

image.png
(以上流程图对应源码在下面(以非公平锁为例,公平锁差不多,不同处会说明)。AQS与ReentrantLock直接关联在 tryAcquire() 处)

  • 以下为非公平锁的申请过程,由 第一步 到 第八步
/**
 * 以下已省略部分代码,部分原有注释更直观,便不翻译
 */
public class ReentrantLock implements Lock, java.io.Serializable {
    
    /**
     * Acquires the lock.
     *
     * <p>Acquires the lock if it is not held by another thread and returns
     * immediately, setting the lock hold count to one.
     *
     * <p>If the current thread already holds the lock then the hold
     * count is incremented by one and the method returns immediately.
     *
     * <p>If the lock is held by another thread then the
     * current thread becomes disabled for thread scheduling
     * purposes and lies dormant until the lock has been acquired,
     * at which time the lock hold count is set to one.
     * 
     * 实现自 Lock 类中的 lock()方法
     */
    public void lock() {
        sync.lock();                                                  //第一步
    }
    
    /** 
     * Synchronizer providing all implementation mechanics
     */
    private final Sync sync;
    
    abstract static class Sync extends AbstractQueuedSynchronizer {
        /**
         * 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) {
                /**
                 * 注意这两个if。在state状态为0,也就是没有被占用的情况下
                 * 会尝试去更改state的值,成功则会被独占
                 */
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            } else if (current == getExclusiveOwnerThread()) {
                /**
                 * 这里是如果当前线程已经是拥有锁了,则 state 数量增加
                 */
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
    
    static final class NonfairSync extends Sync {
        /**
         * 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);
        }
    }
    
    /**
     * 公平锁
     */
    static final class FairSync extends Sync {
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         *
         * 对比一下非公平锁的 nonfairTryAcquire() 方法
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                /**
                 * 公平锁比非公平锁在 tryAcquire() 中多了 !hasQueuedPredecessors()
                 * 这一步骤用来判断同步队列中是否存在有效节点(队列会在下方说明)
                 * 如果返回False,说明当前线程可以直接争取共享资源;
                 * 如果返回True,说明队列中存在有效节点,当前线程必须加入到同步队列中
                 * 这是AQS的逻辑(注意:和队列相关的,都属于AQS的逻辑,代码贴在AQS类里)
                 */
                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;
        }
    }
}

/**
 * 为了方便看,所以把部分AQS的源码贴在这里
 */

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {
        
     /**
     * Sets the thread that currently owns exclusive access.
     * A {@code null} argument indicates that no thread owns access.
     * This method does not otherwise impose any synchronization or
     * {@code volatile} field accesses.
     * @param thread the owner thread
     */
    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }
}

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    
    static final class Node {
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;
    }
    
    /**
     * 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) {                              //第四步
        /**
         * 这里的 tryAcquire() 是在调用第五步 也就是获取锁的过程
         * 如果获取锁失败,则会执行 AQS 的后续逻辑,也就与 ReentrentLock 无关了
         */
        if (!tryAcquire(arg) &&
            /** 
             * 这里的 addWaiter() 是第七步,acquireQueued() 是第八步
             * 这两步实际上就是把当前线程塞到队列里  先记着这么个步骤  后面说
             */
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            //第七、八步
            selfInterrupt();
    }
    
    /**
     * 这个方法是需要被子类具体实现的  所以调用的应该是子类的方法
     */
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
    
    private Node addWaiter(Node mode) {
        /**
         * node内部类会在下面的队列里面说
         */
        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;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
    
    /**
     * 这个方法在源码中的注释太多了就不贴了  可以自行看看 
     */
    public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        /**
         * 这一行代码比较有意思 但是会牵扯到队列的东西 后面合着队列再说
         * 这里先说明一下 : AQS 的同步队列为双向链表,而且第一个结点为虚拟节点
         */
        return h != t && 
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }                                                                            
}
  • 以下为非公平锁的解锁过程

/**
 * 省略部分代码
 */
public class ReentrantLock implements Lock, java.io.Serializable {
    /**
     * Attempts to release this lock.
     *
     * <p>If the current thread is the holder of this lock then the hold
     * count is decremented.  If the hold count is now zero then the lock
     * is released.  If the current thread is not the holder of this
     * lock then {@link IllegalMonitorStateException} is thrown.
     *
     * @throws IllegalMonitorStateException if the current thread does not
     *         hold this lock
     */
    public void unlock() {                                          //第一步
        /**
         * 这里实际上调用的是 AQS 的 release() 
         */
        sync.release(1);
    }
    
    private final Sync sync;
    /**
     * 这里贴出来只是为了说明  Sync 继承自 AQS
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        /**
         * 省略部分代码
         *
         * 在 Sync 内部类中 对于锁的释放只有这一个方法
         * 从这里可以看出 释放锁是不区分公平或非公平锁的
         * 释放锁的代码相对太简单了……  自己看吧 没啥好说的
         */
        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;
        }
    }           
}

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    
    /**
     * 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)) {
            /**
             * 如果能进来,说明锁的释放已经成功了,此时该锁没有被任何线程占用
             * (后面就是 AQS 的逻辑了)
             */
            Node h = head;
            /**
             * 头结点不为空并且头结点的waitStatus不是初始化节点情况,解除线程挂起状态
             * 这里的 h != null 是为了避免 head 还没初始化的情况
             *        因为第一个节点进来之后会被初始化为一个虚节点,没有数据入队就会是
             *        head == null
             * 至于 h.waitStatus != 0, 当 h.waitStatus == 0 代表当前节点的线程是活跃
             * 的,不需要去唤醒,否则是被阻塞了,需要唤醒(这个地方需要结合下面的队列的
             * acquireQueued() 中获取锁的部分结合来看) 
             */
            if (h != null && h.waitStatus != 0)
                /**
                 * 这里是对阻塞的线程做唤醒操作
                 */
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    
    /**
     * 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;
            /**
             * 从尾部节点到队首找到队列第一个 waitStatus < 0 的节点。
             * 这里为什么要从后往前找 不从前往后找? 这里要结合 下面的 addWaiter() 来说
             * (这里代码只粘贴部分)
             *    node.prev = pred;                           一
             *    if (compareAndSetTail(pred, node)) {        二
             *        pred.next = node;                       三
             *        return node;
             *    }
             * 如上代码:1、一、二虽然不是原子的,但是这一部分是在一个死循环中,也就是说
             *         compareAndSetTail() 方法是一定会执行成功的,哪怕这次没有成功
             *         所以一、二也算是原子操作了。所以 node.prev 一定会是前驱节点的
             *         tail,如此从后往前找数据不会有问题
             *          2、二、三也不是原子的,且在这个方法中是没有被弥补,如果从前往
             *         后找,在执行了 compareAndSetTail() 后,如果 pred.next = node; 
             *         还没执行就开始获取到 pred 节点,此时的 pred.next 为null,就会造
             *         成 pred 为尾结点的错觉(这里可以看Node中 next 和 prev 的注释)
             *         3、(保留意见)在cancelAcquire() 方法中,next指针会断开,prev不
             *         会,所以用next来查找是不安全的。但是为什么说保留意见,因为 
             *         acquireQueued() 中 cancelAcquire() 好像不太可能会被执行。
             *         (acquireQueued()在下方)
             */
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            /**
             * 唤醒
             */
            LockSupport.unpark(s.thread);
    }
}  
  • 以下为AQS队列的操作
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    
    /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new node
     */
    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;
            /**
             * 这里通过 compareAndSetTail() 来完成对尾结点的设置
             * 这里是根据对象属性相对于对象的偏移量来做的改变
             */
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        /**
         *  1、如果Pred指针是Null(说明同步队列中没有元素),
         *  2、或者当前Pred指针和Tail指向的位置不同(说明被别的线程已经修改)
         *  就会走到这里
         *
         * 这个方法是和下面的 hasQueuedPredecessors() 方法有关系的 慢慢来
         */
        enq(node);  
        return node;
    }
    
    /**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node's predecessor
     */
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                /**
                 * 如果没有被初始化,需要进行初始化一个头结点出来
                 * 但是初始化的头结点不是当前线程节点,是调用了无参构造函数的节点
                 * addWaiter就是一个在双端链表添加尾节点的操作
                 * 但是要注意,双端链表的头结点是一个无参构造函数的头结点。
                 */
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

    /**
     * hasQueuedPredecessors用于判断公平锁加锁时同步队列中是否存在有效节点
     * 返回False,则当前线程可以争取共享资源;
     * 返回True,则队列中存在有效节点,当前线程必须加入到同步队列中。
     */
    public final boolean hasQueuedPredecessors() {
    
        /**
         * 注意这里源码给的注释  很细节也很重要!  下面会结合判断一起说明
         *
         *
         *  The correctness of this depends on head being initialized
         *  before tail and on head.next being accurate if the current
         *  thread is first in queue.
         */
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
            
            /**
             * 上面已经说过 AQS 的同步队列首节点为虚拟节点,不存储任何信息,只用来站位
             * enq() 方法里面也可以看得出来。真正有数据的是从第二个节点开始的。所以:
             * 当 h == t 时:
             *      1、当h和t都为null,返回false。此时说明队列为空
             *      2、当h和t都指向同一个Node,也返回false。说明此时队列中只有一个dummy 
             *         node,也就是没有线程在队列中
             *
             * 当 h != t 时:
             *       1、如果(s = h.next) == null,同步队列正在有线程进行初始化,但只是进行到
             *          ail指向Head,还未将Head指向Tail,此时队列中有元素,需要返回True
             *       2、如果(s = h.next) != null,则此时队列中至少有一个有效节点。
             *          如果此时 s.thread == Thread.currentThread(),则同步队列的第一个有
             *          效节点中的线程与当前线程相同,那么当前线程可以获取资源
             *          如果s.thread != Thread.currentThread(),说明同步队列的第一个有效节点
             *          线程与当前线程不同,当前线程须加入同步队列
             *
             * (这里需要结合上面的 enq() 一起看。直接贴过来了)
             *   if (t == null) { // Must initialize
             *       if (compareAndSetHead(new Node()))
             *           tail = head;   //下面说的会出现短暂的head != tail 就是这里
             *                          // 因为 CAS 和 tail = head 这两步不是原子性的   
             *   } else {
             *        node.prev = t;
             *        if (compareAndSetTail(t, node)) {
             *            t.next = node;
             *            return t;
             *        }
             *   }
             * 
             * ☆☆☆ 节点入队不是原子操作,所以会出现短暂的head != tail,此时Tail指向最
             * 后一个节点,而且Tail指向Head。如果Head没有指向Tail,这种情况下也需要将相
             * 关线程加入队列中。所以这后半块判断代码是为了解决极端情况下的并发问题。
             */
        return h != t && 
            ((s = h.next) == null || s.thread != Thread.currentThread());
            
            /**
             * 这里说上面又细节又重要的 head 和tail 的赋值
             * 为什么上面要先 获取tail的数据 再获取head的数据?
             * 也就是  Node t = tail; Node h = head; 这个顺序的原因
             *      这里并不是随意写的,这里和判断中的  (s = h.next) == null 与 enq()
             *      中(上面)的 compareAndSetHead(new Node()) 代码有关
             *      很显然源码中这几处是没有控制执行顺序的,所以可能会出现以下几种情况
             *     (compareAndSetHead(new Node()) 太长, 以 AAA 代替)    
* Node t = tail;                  Node t = tail;                Node t = tail;
* Node h = head;                  AAA                           Node h = head;
* AAA                             Node h = head;                AAA
* 此顺序 t 和 h 都为null      此顺序 h不为null,t为null     此顺序h和t都不为null
             *
             * 从上面三种情况可以看出 在通过 h != t 的情况下,h一定不会为null,然后再执行
             * (s = h.next) == null 这条语句,则不会报空指针
             * 如果这两行代码顺序替换  那就可能出现空指针异常 
             * 这里不存在指令重排序 因为这两个都是写指令 如果不清楚就去看 内存访问重排序
             */
    }         
}
  • 以下是线程出队列

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
        
    /**
     * 没错  又是这块代码
     */
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            /**
             * 上面说了在 addWaiter() 中是线程入队列, 且该方法返回了一个Node
             * 那么 acquireQueued() 则是拿到这个Node对象去获取锁,自然也就是出队列了
             */
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            /**
             * 上面两者都为true,则进入到这里用来给线程标记中断状态
             * 因为在 acquireQueued() 中的 parkAndCheckInterrupt() 会清除掉线程
             * 的中断状态。何时响应中断,下面说
             */
            selfInterrupt();
    }
    
    /**
     * 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();
                /**
                 * 如果p是头结点,说明当前节点在真实数据队列的首部,
                 * 就尝试获取锁(因为头结点是虚节点)
                 */
                if (p == head && tryAcquire(arg)) {
                    /**
                      * 获取锁成功,头指针移动到当前node
                      */
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                
                /**
                 * 如果p为头节点且当前没有获取到锁(可能是非公平锁被抢占了)
                 * 或者是p不为头结点,则需要判断当前node是否需要被阻塞
                 *(被阻塞条件:前驱节点的waitStatus为-1),防止无限循环浪费资源
                 * 请注意这两个方法   接下来慢慢说
                 */
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                /**
                 * 这里是和 取消状态 相关的逻辑
                 */
                cancelAcquire(node);
        }
    }
    
    /**
     * Sets head of queue to be node, thus dequeuing. Called only by
     * acquire methods.  Also nulls out unused fields for sake of GC
     * and to suppress unnecessary signals and traversals.
     * @param node the node
     *
     * 这里就是将指定节点设置为虚节点
     */
    private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }
    
    /**
     * 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
     *
     * 靠前驱节点判断当前线程是否应该被阻塞
     */
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        /**
         * 获取前驱结点的节点状态  以下几个状态值 都在 AQS 内部类 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) {   
            /*
             *  ws > 0 代表取消   即: static final int CANCELLED =  1;
             *
             * 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.
             *
             * 设置前驱节点等待状态为SIGNAL
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
    
    /**
     * Convenience method to park and then check if interrupted
     * @return {@code true} if interrupted
     *
     * 用于挂起当前线程,阻塞调用栈,返回当前线程的中断状态
     */
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
    
    /**
     * Cancels an ongoing attempt to acquire.
     *
     * @param node the node
     */
    private void cancelAcquire(Node node) {
        /**
          * Ignore if node doesn't exist
          */
        if (node == null)
            return;

        /**
          *  设置该节点不关联任何线程,也就是虚节点
          */
        node.thread = null;
        Node pred = node.prev;
        /**
          * 通过前驱节点,跳过取消状态的node
          */
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;
        
        /**
         * predNext is the apparent node to unsplice. CASes below will
         * fail if not, in which case, we lost race vs another cancel
         * or signal, so no further action is necessary.
         * 获取过滤后的前驱节点的后继节点
         */
        Node predNext = pred.next;

        /**
         * Can use unconditional write instead of CAS here.
         * After this atomic step, other Nodes can skip past us.
         * 把当前node的状态设置为CANCELLED
         */
        node.waitStatus = Node.CANCELLED;

        /**
         * 如果当前节点是尾节点,将从后往前的第一个非取消状态的节点设置为尾节点
         * 如果更新成功,将tail的后继节点设置为null
         */
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            /**
             * If successor needs signal, try to set pred's next-link
             * so it will get one. Otherwise wake it up to propagate.
             * 如果当前节点不是head的后继节点:
             *    1:判断当前节点前驱节点是否为SIGNAL,
             *    2:如果不是,则把前驱节点设置为SINGAL看是否成功
             * 如果1和2中有一个为true,再判断当前节点的线程是否为null
             * 如果上述条件都满足,把当前节点的前驱节点的后继指针指向当前节点的后继节点
             */
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                /**
                 * 如果当前节点是head的后继节点,或者上述条件不满足,
                 * 那就唤醒当前节点的后继节点
                 */
                unparkSuccessor(node);
            }
    
            node.next = node; // help GC
        }
    }                                                                                    
}
  • 以下为 acquireQueued() 的流程

image.png

  • 以下为 cancelAcquire() 的流程

image.png

   从上面的 cancelAcquire() 流程图可以看出,变动的都是next指针,prev指针好像没动过?实际上 prev 指针的处理是在 shouldParkAfterFailedAcquire() 中进行的,也就是 node.prev = pred = pred.prev; 这行代码。如果又在 cancelAcquire() 中对 prev 进行操作,则可能会使得 prev 指向一个已经移除的 node 造成数据不安全。又因为能进入 shouldParkAfterFailedAcquire() 说明共享资源已经被获取,所以当前节点之前的节点都不会有变化,这个时候去改动 prev 指针会更安全

  • 简单提一下上述源码中的 selfInterrupt()
/**
 * Convenience method to interrupt current thread.
 * 
 * 这里涉及到Java协作式中断
 *     Thread 类中提供了4个关于中断的方法:如下
 *        1、public static boolean interrupted()
 *            实际调用的是下面第四个方法,参数为true,代表清除状态
 *            判断当前线程是否已经中断,并且会清除掉线程的中断状态
 *            也就是说如果一个线程为中断状态,连续调用两次这个方法,第一次为true,第二次
 *          会是false
 *        2、public boolean isInterrupted() 
 *            实际调用的是下面第四个方法,参数为false,代表不清除状态
 *            判断线程是否已经中断
 *        3、public void interrupt()
 *            是唯一能将中断状态设置为 true 的方法
 *        4、private native boolean isInterrupted(boolean ClearInterrupted) 
 *            判断线程中断与否,并根据传参决定是否清除中断状态
 * 
 *  如何理解协作式:调用 interrupt() 方法后给线程标记上中断,但是并不会立即中断,而是需要
 *                在合适的地方来响应中断。而所谓的合适的地方,实际上取决于代码中是否有对
 *                中断状态做判断并给予对应的中断操作,如果说没有调用对应的中断操作的代
 *                码,则线程的中断并不会被响应(简单来说,我给你标记中断了,至于什么时候
 *                处理这个中断,要不要处理这个中断,你自己决定)。
 * 
 *  AQS中,线程在被唤醒后就一直去获取锁了,此过程中并不会响应中断这个操作。
 *        何时响应,要么自主实现中断响应操作,要么调用已有方法来响应中断,比如说
 *        ThreadPoolExecutor 中的 Worker 在调用 run() 时会调用 runWork(),其中会响应
 */ 
static void selfInterrupt() {
    Thread.currentThread().interrupt();
}
  • 关于条件队列

   推荐直接看这个 segmentfault.com/a/119000001… 。这篇博客中提到的bug在JDK10中被修复了