java.util源码解析(一)AQS介绍

一.AQS介绍

AbstractQueuedSynchronizer 它是java.util包实现的基础,甚至可以说它就是锁,相比于Synchronzied关键字实现的内置锁,只实现了排他锁。AQS通过一个类变量 private int state 来表示锁状态,通过继承类实现了多种锁模式,如排它锁,共享锁,以及可重入锁等。
在AQS中通过一个内部类Node 来包装线程,代码如下

static final class Node {
        /** 用来标志线程处在共享模式,临界区可由多个线程进入 */
        static final Node SHARED = new Node();
        /** 用来标志线程处在排他模式 */
        static final Node EXCLUSIVE = null;
        //用来代表当前线程所处状态
        volatile int waitStatus;

        /** waitStatus 的标志位表示,这个线程已经不可用了
        这里很巧秒的只有 cancelled > 0 即状态大于0,说明线程不可用了 */
        static final int CANCELLED =  1;
        /** 表示后继线程需要被唤醒 */
        static final int SIGNAL    = -1;
        /** 表示线程正处在条件等待中 */
        static final int CONDITION = -2;
        /**
         共享锁中的线程状态
         */
        static final int PROPAGATE = -3;
        //当前节点前驱
        volatile Node prev;

        /**
         * 当前节点的后继
         */
        volatile Node next;

        /**
         * 包装的线程
         */
        volatile Thread thread;

        /**
         用来存储当前线程所在的模式(共享或者排他)
         */
        Node nextWaiter;

        /**
         * 如果当前线程正处在共享模式就返回true,判断依据是nextWaiter == shared
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * 返回节点前继
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // 这个构造方法是用来标识共享模式节点的或者作为阻塞队列头节点。
        }

        Node(Thread thread, Node mode) {     // 用来加入到阻塞队列中去
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // 用来加入到条件队列中去
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

看完上面Node类的分析,其中出现了两个队列 等待队列条件队列,这里先把结果抛出来,AQS内部随着会有一个等待队列和多个条件队列。其中阻塞队列由AQS两个内部变量

  • private transient volatile Node head;
    条件队列头节点,这个节点除了初始化时候,采用一个new Node()空节点代替,在这之后,头节点都是当前占有锁资源的节点。

  • private transient volatile Node tail;
    条件队列尾节点,只能由插入节点的时候修改。

组成的,等待队列中的节点(除了头节点)都通过自旋的方式,等待被唤醒,获取锁资源。
而条件阻塞队列是由AQS的另一个内部类ConditionObject来决定,条件队列也是由两个节点构成的

  • private transient Node firstWaiter;

  • private transient Node lastWaiter;

在用Synchronized关键字作为锁的时候,线程随条件进入阻塞池中,唤醒的时候是随机唤醒的。而用AQS作为锁的时候,就是通过ConditionObject来实现随条件阻塞,因为是存放在条件队列中,因此其中的节点唤醒也是可控制的。另外节点被唤醒后就会插入到等待队列中去。

到这里,已经讲清楚了AQS是如何作为显示锁的,以及其中两个关键队列。
显示锁的使用,就要放到它具体的实现中去讲了。

猜你喜欢

转载自blog.csdn.net/mooneal/article/details/81004503