文章中的源码均来自JDK1.8
一、简介
AbstractQueuedSynchronizer(AQS) 是一个队列同步器,可以用来构建锁或者其他同步组件,如 ReentrantLock 等, 它使用一个 int 的成员变量 state 来表示同步状态,通过内置的 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;
/** 节点在等待队列中,当其他节点的线程调用了Condition的signal()方法后节点会从等待队列转到同步队列中 */
static final int CONDITION = -2;
/**
* 表示下一次共享式同步状态获取将会无条件地被传播下去
*/
static final int PROPAGATE = -3;
/**
* 等待状态
*/
volatile int waitStatus;
/**
* 前驱节点
*/
volatile Node prev;
/**
* 后继节点
*/
volatile Node next;
/**
* 获取同步状态的线程.
*/
volatile Thread thread;
/**
* 当线程在Condition等待时的后继节点或者用来表示当前节点上共享还是独占
*/
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;
}
}
三、API简介
同步器的设计是基于模板方法模式的,也就是使用者需要继承同步器并重写其指定的方法,随后将同步器组合到自定义同步组件中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。同步器可重写的方法如下:
- boolean tryAcquire(int arg) 独占式获取同步状态
- boolean tryRelease(int arg) 独占式释放同步状态
- int tryAcquireShared(int arg) 共享式获取同步状态
- boolean tryReleaseShared(int arg) 共享式释放同步状态
- boolean isHeldExclusively() 是否被当前线程独占
使用者除了可重写以上的方法外,还可以通过调用同步器提供的方来是访问或修改同步状态,如下:
- getState() 获取同步状态
- setState(int newState) 设置当前同步状态
- compareAndSetState(int expect, int update) 使用 CAS 设置当前状态
四、CAS 的简单介绍
CAS是一种乐观锁,compare and swap,也就是比较并替换,它的操作包含3个操作数 --- 内存位置(V)、预期原值(A)、新值(B),如果内存位置的值与预期原值相同,则将内存位置的值替换成新值,否则不做任何改变。CAS的原子操作是利用处理器提供的CMPXCHG指令来实现的,它与synchrionized的区别在于CAS是无阻塞的,但是它只能保证一个共享变量的原子操作。其他详细这里不多讲,可自行查找资料。
参考资料:《Java并发编程的艺术》