AbstractQueuedSynchronizer(队列同步器,AQS)源码剖析(一)

1.1简介

        AQS是构建是一个阻塞锁和相关同步器的实现类的底层框架。我们经常用到的ReentrantLock,CountDownLatch,Semaphore等都是基于它来实现的。内部通过一个int类型的成员变量state来控制同步状态,当state=0时,则说明没有任何线程占有共享资源的锁,当state=1时,则说明有线程目前正在使用共享变量,其他线程必须加入同步队列进行等待,AQS内部通过内部类Node构成FIFO的同步队列来完成线程获取锁的排队工作。

1.2内部类Node

        我们来看看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;
    //表示该节点处于等待队列中,当其他线程调用Condition的signal()方法后,
    //CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁
    static final int CONDITION = -2;
    //在共享模式中,该状态标识结点的线程处于可运行状态。
    static final int PROPAGATE = -3;

    //节点的等待状态,包括CANCELLED,SIGNAL ,CONDITION ,PROPAGATE 四种,
    //如果想要修改这个值,可以使用AQS提供CAS进行修改
    volatile int waitStatus;

     //当前节点的前驱节点
    volatile Node prev;

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

    //当前节点的线程
    volatile Thread thread;

    //等待队列中的后继节点
    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() {    // Used to establish initial head or SHARED marker
    }

    Node(Thread thread, Node mode) {     // 默认使用这个构造器
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) { // 在条件队列中使用
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

 1.3成员变量

        接下来我们在来看看AQS中的属性,源码如下

//指向同步队列的头部,为空节点,不存储信息
private transient volatile Node head;

//指向同步队列的尾部
private transient volatile Node tail;

//同步状态,0代表锁未被占用,1代表锁被占用
private volatile int state;

        可以看出三个成员变量都使用了volatile关键字进行修饰,这就确保了多个线程对它的修改都是内存可见的。AQS提供了两个方法来设置和获取同步状态,源码如下

//获取同步状态
protected final int getState() {
    return state;
}

//设置同步状态
protected final void setState(int newState) {
    state = newState;
}

 1.4入队方法

        接下来我们看一看节点入队操作,源码如下

//节点入队操作
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;//获取同步队列尾节点
        if (t == null) { // 如果尾节点为空,则同步队列还没有初始化
            if (compareAndSetHead(new Node()))
                tail = head;//初始化同步队列
        } else {
            node.prev = t;//将尾节点设置为插入节点的前驱节点
            if (compareAndSetTail(t, node)) {//设置当前插入节点为尾节点
                t.next = node;//将旧的尾节点的后继节点指向插入节点
                return t;//返回旧的尾节点
            }
        }
    }
}

        注意,入队操作使用一个死循环,只有成功将结点添加到同步队列尾部才会返回,返回结果是同步队列原先的尾结点。

猜你喜欢

转载自my.oschina.net/u/3475585/blog/1818328