zoukankan      html  css  js  c++  java
  • AQS源码解析

    解读源码本来就是一件极其枯燥乏味的事情 希望你坚持住 过了这道坎 你会看到不一样的风景;

    在解读源码之前我们来讨论一下源码解读的技巧:

    • 跑不起来的源码不读
    • 解决问题就好-目的性
    • 一条线索到底
    • 无关细节略过

    1、跑不起来的源码不读

    因为大部分源码都会用到好多的设计模式,这就促使如果源码跑不起来,单纯的看是很难看的懂,如果可以跑起来你就可以Ctrl鼠标单击方法跟进去,这样就会事半功倍;

    2、解决问题就好-目的性

    在实际工作中你可能会接手一个改过不知道多少遍的代码,这时你就要搞清楚,如果单纯的只是为解决问题,就没有必要去看细节;

    3、一条线索到底

    读源码一定要一条线跟到底,不要只是都表面,我们直到当一个程序跑起来,可能会很大,很多方法点进去还会调用其他的方法,你不用每个方法都看一遍再进去找,尽量一条线索跟到底,就读一个方法,由浅入深再看一遍;

    4、无关细节略过

    有些边缘性的东西,在你读第一遍的时候,没有必要的时候,可以略过;

    接着上一篇的ReentrantLock,它的内部实现的核心是 CAS操作+volatile;

    下面我们就来一步一步解读源码:


    一、准备工作(需要了解 ReentrantLock 用法,ReentrantLock的构造方法, 类之间的关系等等)

    1、一般的使用方法以及构造方法:

    一般使用方法:

    final Lock lock = new ReentrantLock();
    lock.lock();

    构造方法(因为有排它锁和公平锁的概念,构造方法分为默认构造方法和有参构造方法):

     /**
         * 默认构造方法
         */
        public ReentrantLock() {
            sync = new NonfairSync();
        }
        /**
         * 可以传参数 控制锁的公平性
         * @param fair
         */
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }

    2、下面看一下ReentrantLock类:  

    ReentrantLock这个类有三个内部类:Sync、NonfairSync、FairSync,他们之间存在了一系列的关系

    因为 ReentrantLock 默认为非公平锁,类图就以 NonfairSync为例(公平锁的类图类似):

     

    二、源码分析<一>:lock -> acquire -> tryAcquire -> nonfairTryAcquire->返回Boolean类型值

    首先你要学会画泳道图,下面先简单看一下泳道图:

    下面看一下代码的执行顺序:

    2.1、调用Sync类的lock方法:

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

    2.2、Sync类的lock方法由具体的子类(NonfairSync)来实现:

    首先进行  compareAndSetState 操作将state修改为1,并把当前线程设置为这把锁的独占线程;

    具体的代码如下:

      /**内部类NonfairSync(非公平锁)继承了Sync*/
            static final class NonfairSync extends java.util.concurrent.locks.ReentrantLock.Sync {
                /**
                 * 尝试着直接去修改state 修改成功证明当前没有线程持有这把锁 然后设置当前独占访问的线程
                 * 如果修改失败
                 */
                final void lock() {
                    if (compareAndSetState(0, 1))
                        setExclusiveOwnerThread(Thread.currentThread());
                    else
                    acquire(1);
                }
            }

    2.3、AQS.compareAndSetState(0, 1)

     //执行CAS操作 使用了unsafe类的原子操作 多线程的情况下只能有一个线程修改成功
            protected final boolean compareAndSetState(int expect, int update) {
                // See below for intrinsics setup to support this
                return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
            }

    2.4、AQS.acquire(1)     (当compareAndSetState修改当前线程独占失败)

    /**
             * 这里是先执行tryAcquire(arg)方法,也就是尝试获取锁的功能:
             * 1.如果返回tryAcquire(arg)为true说明获取锁成功,也就是!tryAcquire(arg)为false,
             * 则不会执行后面的acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法了,直接执行业务代码去了
             * 
             * 2.如果返回tryAcquire(arg)为false说明获取锁失败,也就是!tryAcquire(arg)为true,
             * 则会再去执行后面的acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法
             * 
             * 所以我们下面要分析下tryAcquire(arg)方法做了什么
             * @param arg
             */
            public final void acquire(int arg) {
                if (!tryAcquire(arg) &&
                        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                    selfInterrupt();
            }

    2.5、NonfairSync.tryAcquire(arg)   尝试获取锁

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

    2.6、Sync.nonfairTryAcquire(acquires)

    final boolean nonfairTryAcquire(int acquires) {
                    final Thread current = Thread.currentThread();
                    //获取当前同步锁的状态,判断state如果等于0 说明当前没有线程独占这把锁
                    int c = getState();
                    if (c == 0) {
                        //CAS操作修改state = 1 并设置为当前线程独占 返回true
                        if (compareAndSetState(0, acquires)) {
                            setExclusiveOwnerThread(current);
                            return true;
                        }
                    }
                    //因为这里有锁重入的概念 所以判断当前持有这把锁的线程和current是不是同一个线程
                    else if (current == getExclusiveOwnerThread()) {
                        //如果是同一个线程 state+1
                        int nextc = c + acquires;
                        if (nextc < 0) // overflow
                            throw new Error("Maximum lock count exceeded");
                        setState(nextc);
                        return true;
                    }
                    return false;
                }
            }

    三、源码分析<二>:tryAcquire-> addWaiter-> acquireQueued

    解读AQS源码之前你需要了解AQS的核心是CAS+volatile,这里所谓的CAS是因为在多线程的情况下,没有使用synchronized来同步而是使用了大量的CAS操作(例如线程节点获取锁和加到尾节点上都使用了CAS操作),内部还维护了一个双向链表有头节点和尾节点,定义了一个  private volatile int state 具体的含义由子类自己决定,ReentrantLock中state的含义是:当state等于0时证明当前锁没有任何的线程占有,state=1时,证明有线程占有这把锁,记住这里因为ReentrantLock具有可重入性,所以重入一次state加一,当state减少至0时,释放锁。

    • 利用CAS操作替换synchronized锁整条链表:

    • 内部维护了一条双向链表,有头节点和尾节点,新进来的线程节点利用CAS操作获取当前锁,如果获取失败就会将当前线程节点加到等待队列,这里也使用了CAS操作,尽管此过程没有加锁,但是CAS操作时原子操作保证了原子性,同时使用volatile修饰了state,保证了线程获取同步状态的可见性;

     

    首先也简单画一个流程图:

     3.1、AQS.addWaiter(Node.EXCLUSIVE)方法 如果tryAcquire的返回值为true获取同步锁成功否则获取同步锁失败) 当返回false执行addWaiter方法:

    • 看addWaiter代码之前先看一下Node类(AQS的内部类):
    static final class Node {
            //定义了一个node节点
            static final Node EXCLUSIVE = null;
    
            /** waitStatus value to indicate thread has cancelled  waitStatus值表示线程已被取消*/
            static final int CANCELLED =  1;
            /** waitStatus value to indicate successor's thread needs unparking waitStatus值,表示后续线程需要解锁*/
            static final int SIGNAL    = -1;
            /** waitStatus value to indicate thread is waiting on condition  waitStatus值表示线程正在等待状态*/
            static final int CONDITION = -2;
            /**
             * waitStatus value to indicate the next acquireShared should
             * unconditionally propagate  状态需要向后传播
             */
            static final int PROPAGATE = -3;
            //构造方法
            Node(Thread thread, Node mode) {     // Used by addWaiter
                this.nextWaiter = mode;
                this.thread = thread;
            }
    
            //头节点
            private transient volatile Node head;
            //尾节点
            private transient volatile Node tail;
            /**
             * The synchronization state. 锁的同步状态
             */
            private volatile int state;
    
            volatile int waitStatus;
            //前一个node节点
            volatile Node prev;
            //后一个node节点
            volatile Node next;
            //线程
            volatile Thread thread;
            //后面等待的节点
            Node nextWaiter;
        }
    • 接下来看一下 AQS.addWaiter(Node.EXCLUSIVE), arg)从方法名可以看出添加一个排它的等待者):
    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;
                    //使用CAS操作 将等待队列的双向链表 当前节点设置为尾部
                    if (compareAndSetTail(pred, node)) {
                        //之前的尾部的下一个节点指向新加入的节点
                        pred.next = node;
                        //返回节点
                        return node;
                    }
                }
                //如果队列没有尾节点
                enq(node);
                return node;
            }

    3.2、AQS.enq(node) 方法  如果尾节点为空,执行完整的添加节点操作:

    /**
         * 执行完整的添加节点操作
         * @param node
         * @return
         */
        private Node enq(final Node node) {
            for (;;) {
                Node t = tail;
                //判断为节点是否为空
                if (t == null) { // Must initialize
                    //CAS操作把当前线程节点设置为头节点
                    if (compareAndSetHead(new Node()))
                        //此时头节点和尾节点都为当前节点
                        tail = head;
                } else {
                    //如果此时已经有别的节点加入 CAS操作 把当前节点设置为尾节点
                    node.prev = t;
                    if (compareAndSetTail(t, node)) {
                        t.next = node;
                        return t;
                    }
                }
            }
        }

    3.3、AQS.acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

    首先获得当前node的前驱节点,判断前驱节点是否为head;调用  tryAcquire  方法获取同步锁,成功了就将当前节点设置为头节点,如果前驱节点不为头节点或者获取同步锁失败,需要调用  shouldParkAfterFailedAcquire方法 ,通过内部的 waitStatus 的值来判断是否需要阻塞当前线程。

     //以排他不可中断模式获取线程队列
        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;
                    }
                    //获取同步状态失败 判断是否需要阻塞线程,内部通过状态判断,waitStatus为-1返回true
                    if (shouldParkAfterFailedAcquire(p, node) &&
                            parkAndCheckInterrupt())
                        //线程进入阻塞状态
                        interrupted = true;
                }
            } finally {
                if (failed)
                    //取消正在进行的获取尝试
                    cancelAcquire(node);
            }
        }

    3.4、通过内部的 waitStatus 的值来判断是否需要阻塞当前线程;  

    • waitStatus = -1时  返回true ,当前线程需要阻塞 ,调用 parkAndCheckInterrupt  -> LockSupport.park(this);
    • waitStatus =  1时  表示前置节点已经等待超时或者已经被中断了 ,这时需要将其从队列中删除;  
    • waitStatus = -2时  表示当前节点在condition(同步队列)中等待;
    • waitStatus = -3时  表示状态需要向后传播,尽在共享锁的条件下使用;
    /**
         * 通过内部waitStatus 的状态来判断线程是否需要阻塞
         * @param pred 前驱节点
         * @param node 当前节点
         * @return 返回 boolean 类型值
         */
        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;
                //CANCELLED =  1; 表示前驱节点被取消了  跳过前一个节点重试
            if (ws > 0) {
                do {
                    node.prev = pred = pred.prev;
                } while (pred.waitStatus > 0);
                pred.next = node;
            } else {
                // CONDITION = -2   CONDITION = -3 设置当前 node 的前驱节点的waitStatus = -1(SIGNAL)
                compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
            }
            return false;
        }

    总结:AQS源码主要核心在于CAS操作和volatile  利用CAS操作代替了Synchronized锁整条链表的操作 ,volatile 修饰的state(锁的状态)下维护着一个双向链表;

  • 相关阅读:
    VScode 关闭回车后自动格式化代码
    『转载』专利申请
    『转载』 免费公用DNS服务及三大运营商DNS大全 含IPV4和IPV6
    正则表达式和元字符
    随机背景图
    秒表
    数组相关的函数
    对象的结构语法
    数组的结构语法
    展开合并运算符
  • 原文地址:https://www.cnblogs.com/dongl961230/p/13373137.html
Copyright © 2011-2022 走看看