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;//返回旧的尾节点 } } } }
注意,入队操作使用一个死循环,只有成功将结点添加到同步队列尾部才会返回,返回结果是同步队列原先的尾结点。