文章中的源码均来自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并发编程的艺术》