看一下AbstractQueuedSynchronizer(以下简称AQS)的子类就可以知道,J.U.C中宣传的封装良好的同步工具类Semaphore、CountDownLatch、ReentrantLock、ReentrantReadWriteLock、FutureTask等虽然各自都有不同特征,但是其内部的实现都与AQS分不开。所以分析AQS的实现原理对其余显示锁或则同步工具类的理解很重要。
这一篇blog主要分析AQS的实现中的重要属性和内部类。尤其是对于ReentrantLock和ReentrantReadWriteLock,其lock()方法和unlock()方法的实现最终都是由AQS同步器实现的,由此可见分析AQS类的重要性可见一斑。
在AQS中,我们先看属性远比看方法来的更加容易理解这个类的作用。首先看AQS类的主要属性:
//等待队列的头指针
private transient volatile Node head;
//等待队列的尾指针
private transient volatile Node tail;
//同步器的状态位,注意这里state是声明了volatile,保证了可视性
private volatile int state;
注释其实已经告诉我们了,Node类型的 head 和 tail 是一个FIFO的wait queue;一个int类型的状态位state。到这里也能猜到AQS对外呈现(或者说声明)的主要行为就是由一个状态位和一个有序队列来配合完成。
state属性
对于state状态的管理,在AQS中只通过三个方法来实现:
java.util.concurrent.locks.AbstractQueuedSynchronizer.getState();
java.util.concurrent.locks.AbstractQueuedSynchronizer.setState(int);
java.util.concurrent.locks.AbstractQueuedSynchronizer.compareAndSetState(int, int);
前面两个函数其实就是get和set方法。第三个函数其实是通过Unsafe类实现CAS设置状态值,CAS+volatile 保证了state变量的线程安全。
Node结点
前面还提到了同步器的实现还依赖于一个FIFO的队列。队列中的元素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;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile int waitStatus;
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() { // Used to establish initial head or SHARED marker
}
/** 构造器 */
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
/** 构造器 */
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
Node类主要有5个属性:
volatile int waitStatus;//
volatile Node prev;//
volatile Node next;//
volatile Thread thread;//
Node nextWaiter;//
以上五个成员变量主要负责保存该节点的线程引用,同步等待队列(以下简称sync队列)的前驱和后继节点,同时也包括了同步状态。
对这5个变量的解释如下:
属性名称 | 描述 |
---|---|
int waitStatus | 表示节点的状态。其中包含的状态有: 1.CANCELLED,值为1,表示当前的线程被取消; 2.SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark; 3.CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中; 4.PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行; 5.值为0,表示当前节点在sync队列中,等待着获取锁。 |
Node prev | 前驱节点,比如当前节点被取消,那就需要前驱节点和后继节点来完成连接。 |
Node next | 后继节点。 |
Node nextWaiter | 存储condition队列中的后继节点。 |
Thread thread | 入队列时的当前线程。 |
节点成为sync队列和condition队列构建的基础,在同步器中就包含了sync队列。同步器拥有三个成员变量:sync队列的头结点head、sync队列的尾节点tail和状态state。对于锁的获取,请求形成节点,将其挂载在尾部,而锁资源的转移(释放再获取)是从头部开始向后进行。对于同步器维护的状态state,多个线程对其的获取将会产生一个链式的结构。
重要函数的源码解析
获取锁相关函数
acquire(int arg);//以独占模式获取对象,忽略中断。
acquireInterruptibly(int arg);//以独占模式获取对象,如果被中断则中止。
acquireShared(int arg);//以共享模式获取对象,忽略中断。
acquireSharedInterruptibly(int arg);//以共享模式获取对象,如果被中断则中止。
tryAcquire(int arg);//试图在独占模式下获取对象状态。
tryAcquireNanos(int arg, long nanosTimeout);//试图以独占模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。
tryAcquireShared(int arg);//试图在共享模式下获取对象状态。
tryAcquireSharedNanos(int arg, long nanosTimeout);//试图以共享模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。
释放锁相关函数
release(int arg);//以独占模式释放对象。
releaseShared(int arg);//以共享模式释放对象
tryRelease(int arg);//试图设置状态来反映独占模式下的一个释放。
tryReleaseShared(int arg);//试图设置状态来反映共享模式下的一个释放。