AQS源码解读

参考自:http://www.cnblogs.com/waterystone/p/4920797.html,
https://blog.csdn.net/qq_30572275/article/details/80297047
AQS全名AbstractQueuedSynchronizer,就和字面上一样,是维护了一个队列的同步器框架。
AQS源码解读
AQS类里有一个Node类,对线程进行了封装。看看里面的字段:

static final class Node {

    // 共享模式下等待的标记
    static final Node SHARED = new Node();
    // 独占模式下等待的标记
    static final Node EXCLUSIVE = null;

    // 线程的等待状态 表示线程已经被取消
    static final int CANCELLED =  1;
    // 线程的等待状态 表示后继线程需要被唤醒
    static final int SIGNAL    = -1;
    // 线程的等待状态 表示线程在Condtion上
    static final int CONDITION = -2;

    // 表示下一个acquireShared需要无条件的传播
    static final int PROPAGATE = -3;


    volatile int waitStatus;


    volatile Node prev;


    volatile Node next;

    /**
     * 当前节点的线程,初始化后使用,在使用后失效 
     */
    volatile Thread thread;

    /**
     * 链接到等在等待条件上的下一个节点,或特殊的值SHARED,因为条件队列只有在独占模式时才能被访问,
     * 所以我们只需要一个简单的连接队列在等待的时候保存节点,然后把它们转移到队列中重新获取
     * 因为条件只能是独占性的,我们通过使用特殊的值来表示共享模式
     */
    Node nextWaiter;

    /**
     * 如果节点处于共享模式下等待直接返回true
     */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }

    /**
     * 返回当前节点的前驱节点,如果为空,直接抛出空指针异常
     */
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

    Node() {    // 用来建立初始化的head 或 SHARED的标记
    }

    Node(Thread thread, Node mode) {     // 指定线程和模式的构造方法
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) { // 指定线程和节点状态的构造方法
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

然后是AQS类里的其他几个字段(volatile和CAS可以在博客里的其他文章看)
AQS源码解读

好了,接下来就是开始获取里,直接上源码:
AQS源码解读
注释里说的很清楚,以独占模式尝试获取锁,忽略里中断。里面的tryAcquire()方法是用来给我们自己实现的,毕竟AQS是一个框架(可以去我的博客里看ReentrentLock的tryAcquire()是怎么实现的)。后面接着的是addWaiter()方法:
AQS源码解读
AQS源码解读
AQS源码解读
AQS源码解读
然后看acquireQueued()方法:
AQS源码解读
AQS源码解读
shouldParkAfterFailedAcquire和parkAndCheckInterrupt两个方法用来让当前节点找到一个合适的地方开始等待
AQS源码解读
AQS源码解读
最后总结下流程吧,参考大佬的图:
1.调用自定义同步器的tryAcquire()尝试直接去获取资源,如果成功则直接返回;
2.没成功,则addWaiter()将该线程加入等待队列的尾部,并标记为独占模式;
3.acquireQueued()使线程在等待队列中休息,有机会时(轮到自己,会被unpark())会去尝试获取资源。获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
4.如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。
AQS源码解读
接下来看如何释放:
AQS源码解读
改写tryRelease()方法,成功后,释放资源,用unparkSuccessor(h)来释放并唤醒下一个节点
AQS源码解读
至于共享模式的话,其实就是在获取成功后会唤醒后面的节点,共享模式释放的时候,也会唤醒后继。(这个时候PROPAGATE这个值才会被用到).
最后,个人觉得的话,这东西就是一个队列,内部一直循环请求,再如ReentrantLock,其实内部就是一个acquire(1),只获取一个资源,本身有一个state,state=0的话就是锁没被占用,!=0就表示锁被占用里。
另外,有关读写锁ReentrantReadWriteLock的详解,可以参考大佬的文章:
https://blog.csdn.net/qq_19431333/article/details/70568478
顺嘴提一句,关于setHeadAndPropagate()方法,countDownLatch里也用到里这个,countdownLatch就是开始时候赋一个值,然后内部循环查询是否这个是归零里,如果归零里就会调用这个方法将后续等待着的节点都唤醒,就实现里所谓的等大家都准备好了再行动。