AQS是AbstractQueuedSynchronizer的缩写,AQS是Java并包里大部分同步器的基础构件,利用AQS可以很方便的创建锁和同步器。它封装了一个状态,提供了一系列的获取和释放操作,这些获取和释放操作都是基于状态的。它的基本思想是由AQS负责管理同步器类中的状态,其他的同步器比如可重入锁ReentrantLock, 信号量Semaphore基于各自的特点来调用AQS提供了基础能力进行状态的同步。
在AQS的Javadoc里面提到它是CLHLock的变种,在聊聊高并发(八)实现几种自旋锁(三) 这篇文章中我们说了如何利用CLH锁来构件自旋锁,回顾一下CLHLock的一些基本特点:
1. CLHLock是一种队列自旋锁的实现,提供了FIFO先来先服务的公平性
2. 利用一个原子变量AtomicReference tail的CAS操作来构件一个虚拟的链式结构
3. 节点Node维护一个volatile状态,维护一个prev指针指向前一个节点,获取锁时每个线程在prev节点的状态上自旋
4. 当线程释放锁时,只需要修改自身状态即可,后续节点会观察到volatile状态的改动而获取锁
AQS既然是CLHLock的一种变种,那么
1. 也维护以了一个基本的队列结构
2. 也是提供了一个Tail指针从队尾通过CAS操作入队列。
3. 提供了一个volatile类型的int值来维护状态
-
public abstract class AbstractQueuedSynchronizer
-
extends AbstractOwnableSynchronizer
-
implements java.io.Serializable {
-
private transient volatile Node head;
-
private transient volatile Node tail;
-
private volatile int state;
-
protected final int getState() {
-
return state;
-
}
-
-
protected final void setState(int newState) {
-
state = newState;
-
}
-
protected final boolean compareAndSetState(int expect, int update) {
-
// See below for intrinsics setup to support this
-
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
-
}
-
..................
-
}
与标准CLHLock实现不同的是,AQS不是一个自旋锁,它提供了更加丰富的语意:
1. 提供了独享(exclusive)方式和共享(share)方式来获取/释放,比如锁是独占方式的,信号量semaphore是共享方式的,可以有多个线程进入临界区
2. 支持可中断和不可中断的获取/释放
3. 支持普通的和具有时间限制的获取/释放
4. 提供了自旋和阻塞的切换,可以先自旋,如果等待时间长,可以阻塞
-
/**
-
* The number of nanoseconds for which it is faster to spin
-
* rather than to use timed park. A rough estimate suffices
-
* to improve responsiveness with very short timeouts.
-
*/
-
static final long spinForTimeoutThreshold = 1000L;
AQS定义了两个内部类来辅助它的实现,一个是Node定义了队列中的节点,另一个是ConditionObject,是Condition接口的实现类,负责管理条件队列。关于条件队列更多内容可以看这篇 聊聊高并发(十四)理解Java中的管程,条件队列,Condition以及实现一个阻塞队列
先看下Node类,它比CLHLock中的Node有更多属性,除了完成基本的队列功能,还维护了是独享还是共享的模式信息
1. 维护了一个Node SHARED引用表示共享模式
2. 维护了一个Node EXCLUSIVE引用表示独占模式
3. 维护了几种节点等待的状态 waitStatus, 其中CANCELLED = 1是正数,表示取消状态,SIGNAL = -1,CONDITION = -2, PROPAGATE = -3都是负数,表示节点在条件队列的某个状态,SIGNAL表示后续节点需要被唤醒
4. 维护了Node prev引用,指向队列中的前一个节点,通过Tail的CAS操作来创建
5. 维护了Node next引用,指向队列中的下一个节点,也是在通过Tail入队列的时候设置的,这样就维护了一个双向队列
6. 维护了一个volatile的Thread引用,把一个节点关联到一个线程
7. 维护了Node nextWaiter引用,指向在条件队列中的下一个正在等待的节点,是给条件队列使用的。值得注意的是条件队列只有在独享状态下才使用
-
static final class Node {
-
/** Marker to indicate a node is waiting in shared mode */
-
static final Node SHARED = new Node();
-
/** Marker to indicate a node is waiting in exclusive mode */
-
static final Node EXCLUSIVE = null;
-
/** waitStatus value to indicate thread has cancelled */
-
static final int CANCELLED = 1;
-
/** waitStatus value to indicate successor's thread needs unparking */
-
static final int SIGNAL = -1;
-
/** waitStatus value to indicate thread is waiting on condition */
-
static final int CONDITION = -2;
-
/**
-
* waitStatus value to indicate the next acquireShared should
-
* unconditionally propagate
-
*/
-
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;
-
}
-
}
再看一下ConditionObject,它是条件Condition接口的具体实现,维护了一个条件队列,条件队列是通过Node来构件的一个单向链表结构。底层的条件操作(等待和唤醒)使用LockSupport类来实现,在这篇中我们说了LockSupport底层使用sun.misc.Unsafe来提供条件队列的park和unpark操作。聊聊高并发(十七)解析java.util.concurrent各个组件(一) 了解sun.misc.Unsafe类
1. 维护了一个Node firstWaiter引用指向条件队列的队首节点
2. 维护了一个Node lastWaiter引用指向条件队列的队尾节点
3. 条件队列支持节点的取消退出机制,CANCELLED节点来表示这种取消状态
4. 支持限时等待机制
5. 支持可中断和不可中断的等待
我们来看几个典型的条件队列的操作实现
往条件队列里面加入一个等待节点,这个是await()方法的基本操作
1. 判断尾节点的状态是不是等待某个条件的状态(CONDITION),如果不是,就把CANCELLED节点从队列中踢出,然后把自己标记为尾节点
-
public class ConditionObject implements Condition, java.io.Serializable {
-
/** First node of condition queue. */
-
private transient Node firstWaiter;
-
/** Last node of condition queue. */
-
private transient Node lastWaiter;
-
/**
-
* Adds a new waiter to wait queue.
-
* @return its new wait node
-
*/
-
private Node addConditionWaiter() {
-
Node t = lastWaiter;
-
// If lastWaiter is cancelled, clean out.
-
if (t != null && t.waitStatus != Node.CONDITION) {
-
unlinkCancelledWaiters();
-
t = lastWaiter;
-
}
-
Node node = new Node(Thread.currentThread(), Node.CONDITION);
-
if (t == null)
-
firstWaiter = node;
-
else
-
t.nextWaiter = node;
-
lastWaiter = node;
-
return node;
-
}
-
private void unlinkCancelledWaiters() {
-
Node t = firstWaiter;
-
Node trail = null;
-
while (t != null) {
-
Node next = t.nextWaiter;
-
if (t.waitStatus != Node.CONDITION) {
-
t.nextWaiter = null;
-
if (trail == null)
-
firstWaiter = next;
-
else
-
trail.nextWaiter = next;
-
if (next == null)
-
lastWaiter = trail;
-
}
-
else
-
trail = t;
-
t = next;
-
}
-
}
-
.................
-
}
从条件队列中唤醒一个节点,实际上doSignal只是把一个节点从条件队列中移除,然后加入到同步队列,并设置它在同步队列的前置节点的waitStatus = SIGNAL, 如果设置失败或者取消在条件队列等待,直接把这个节点的线程unpark唤醒,需要注意的是unpark操作只是把线程从等待状态转化为可运行状态,并不直接获得锁。
-
public final void signal() {
-
if (!isHeldExclusively())
-
throw new IllegalMonitorStateException();
-
Node first = firstWaiter;
-
if (first != null)
-
doSignal(first);
-
}
-
/**
-
* Removes and transfers nodes until hit non-cancelled one or
-
* null. Split out from signal in part to encourage compilers
-
* to inline the case of no waiters.
-
* @param first (non-null) the first node on condition queue
-
*/
-
private void doSignal(Node first) {
-
do {
-
if ( (firstWaiter = first.nextWaiter) == null)
-
lastWaiter = null;
-
first.nextWaiter = null;
-
} while (!transferForSignal(first) &&
-
(first = firstWaiter) != null);
-
}
-
final boolean transferForSignal(Node node) {
-
/*
-
* If cannot change waitStatus, the node has been cancelled.
-
*/
-
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
-
return false;
-
/*
-
* Splice onto queue and try to set waitStatus of predecessor to
-
* indicate that thread is (probably) waiting. If cancelled or
-
* attempt to set waitStatus fails, wake up to resync (in which
-
* case the waitStatus can be transiently and harmlessly wrong).
-
*/
-
Node p = enq(node);
-
int ws = p.waitStatus;
-
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
-
LockSupport.unpark(node.thread);
-
return true;
-
}
Java线程的几种状态如下
支持中断的等待操作, 主要做了两个事情:新建一个Node进入条件队列等待被唤醒;从同步队列中移除并释放锁。它会相应线程的中断抛出中断异常,并且记录中断状态
-
public final void await() throws InterruptedException {
-
if (Thread.interrupted())
-
throw new InterruptedException();
-
Node node = addConditionWaiter();
-
int savedState = fullyRelease(node);
-
int interruptMode = 0;
-
while (!isOnSyncQueue(node)) {
-
LockSupport.park(this);
-
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
-
break;
-
}
-
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
-
interruptMode = REINTERRUPT;
-
if (node.nextWaiter != null) // clean up if cancelled
-
unlinkCancelledWaiters();
-
if (interruptMode != 0)
-
reportInterruptAfterWait(interruptMode);
-
}
不可中断的等待,也是先进入条件队列等待,并从同步队列出队列,释放锁。但是它不相应线程中断状态
-
public final void awaitUninterruptibly() {
-
Node node = addConditionWaiter();
-
int savedState = fullyRelease(node);
-
boolean interrupted = false;
-
while (!isOnSyncQueue(node)) {
-
LockSupport.park(this);
-
if (Thread.interrupted())
-
interrupted = true;
-
}
-
if (acquireQueued(node, savedState) || interrupted)
-
selfInterrupt();
-
}
限时等待,也是先进入条件队列等待,然后释放锁。轮询等待时间,当超时后再次进入同步队列,等待获得锁。如果获得了锁,就返回false. 如果在等待时被唤醒,就进入同步队列,等待获得锁,如果获得锁就返回true
-
public final boolean await(long time, TimeUnit unit)
-
throws InterruptedException {
-
if (unit == null)
-
throw new NullPointerException();
-
long nanosTimeout = unit.toNanos(time);
-
if (Thread.interrupted())
-
throw new InterruptedException();
-
Node node = addConditionWaiter();
-
int savedState = fullyRelease(node);
-
long lastTime = System.nanoTime();
-
boolean timedout = false;
-
int interruptMode = 0;
-
while (!isOnSyncQueue(node)) {
-
if (nanosTimeout <= 0L) {
-
timedout = transferAfterCancelledWait(node);
-
break;
-
}
-
if (nanosTimeout >= spinForTimeoutThreshold)
-
LockSupport.parkNanos(this, nanosTimeout);
-
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
-
break;
-
long now = System.nanoTime();
-
nanosTimeout -= now - lastTime;
-
lastTime = now;
-
}
-
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
-
interruptMode = REINTERRUPT;
-
if (node.nextWaiter != null)
-
unlinkCancelledWaiters();
-
if (interruptMode != 0)
-
reportInterruptAfterWait(interruptMode);
-
return !timedout;
-
}
AQS使用了Unsafe直接操作内存来对字段进行CAS操作和设置值。
-
private static final Unsafe unsafe = Unsafe.getUnsafe();
-
private static final long stateOffset;
-
private static final long headOffset;
-
private static final long tailOffset;
-
private static final long waitStatusOffset;
-
private static final long nextOffset;
-
static {
-
try {
-
stateOffset = unsafe.objectFieldOffset
-
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
-
headOffset = unsafe.objectFieldOffset
-
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
-
tailOffset = unsafe.objectFieldOffset
-
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
-
waitStatusOffset = unsafe.objectFieldOffset
-
(Node.class.getDeclaredField("waitStatus"));
-
nextOffset = unsafe.objectFieldOffset
-
(Node.class.getDeclaredField("next"));
-
} catch (Exception ex) { throw new Error(ex); }
-
}