AQS(AbstractQueuedSynchronizer的缩写,下文都用AQS代替)属于java显示锁的一个关键的抽象类,同样也是java线程中一个很重要的抽象类,我们使用的ReentrantLock,ReentrantReadWriteLock等都继承了该类。他是由Java为我们提供的一个底层同步工具类,是一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架,它主要使用一个int类型的 state 变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态。我们可以看看AQS中都为我们提供了哪些类和方法:
可以看到AQS中有两个内部类,分别是Node和ConditionObject。这篇文章主要对AQS做一些描述,和他的Node内部类的解析。
首先我们看看Node这个内部类
AQS中的Node内部类的作用就是以队列的方式来存放线程节点的,我们直接上代码
static final class Node { static final Node SHARED = new Node(); //指示节点在共享模式下等待的标记 static final Node EXCLUSIVE = null; //指示节点在独占模式下等待的标记 static final int CANCELLED = 1; //表示线程已被取消的waitStatus值 static final int SIGNAL = -1; //表示后续线程需要取消阻塞的waitStatus值 static final int CONDITION = -2; //表示线程在条件下等待的waitStatus值 static final int PROPAGATE = -3; //表示下一个获取共享应无条件传播的waitStatus值 volatile int waitStatus; //状态字段,仅接受以上4个值和默认的0 volatile Node prev; //指向当前节点的前置节点 volatile Node next; //指向当前节点的后置节点 volatile Thread thread; //当前节点的线程 Node nextWaiter; //指向下一个条件等待节点 final boolean isShared() { return nextWaiter == SHARED; } //获取当前节点的前置节点 final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { //用于建立初始头节点或共享标记 } //addWaiter方法所使用 Node(Thread thread, Node mode) { this.nextWaiter = mode; this.thread = thread; } //用于Condition中使用 Node(Thread thread, int waitStatus) { this.waitStatus = waitStatus; this.thread = thread; } }
可以看到Node内部类中的代码并不复杂,其中只定义了一些常量和构造方法,为了方便理解我也写了一些注释。
Node内部类主要通过 waitStatus 来表示当前Node节点的状态,它主要有5种状态,分别是:
- INITAL。值为0,初始状态。
- CANCELLED。值为1 ,表示线程已被取消。当该线程由于超时或中断,此节点被取消。节点永远不会离开此状态。特别是状态为已取消节点的线程再也不会阻塞。
- SIGNAL。值为 -1,表示后续线程需要取消阻塞。此节点的后继节点(或将很快)被阻塞(通过park),因此当前节点在释放或取消时必须取消对其后继节点的阻塞。为了避免争用,获取方法必须首先表明它们需要一个信号,然后重试原子获取,然后在失败时阻塞。
- CONDITION。值为 -2,表示线程在条件下等待。节点处于条件等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了signal()方法后,该节点从等待队列中转移到同步队列中,加入到对同步状态的获取中。
- PROPAGATE。值为 -3,表示下一次的共享状态会被无条件的传播下去。
其中有三个为 Node 的变量,分别为 prev ,next ,nextWaiter。
prev :指向同步队列中当前Node节点的前置节点。
next :指向同步队列中当前Node节点的后置节点;
nextWaiter :指向条件等待队列中当前Node节点的后继节点(如果当前节点是共享的,那么这个字段是一个SHARED常量,也就是说节点类型(独占和共享)和等待队列中的后继节点共用一个字段。比如说当前节点A是共享的,那么它的这个字段是shared,也就是说在这个等待队列中,A节点的后继节点也是shared。如果A节点不是共享的,那么它的nextWaiter就不是一个SHARED常量,即是独占的)。
它也用 Thread 保存了当前线程。