1 概述
AQS提供了一个基于FIFO队列实现锁和同步器的基本框架。
j.c.u中使用AQS的类有:
- CountDownLatch.Sync
- ThreadPoolExecutor.Worker
- ReentrantLock.FairSync/NonfairSync
- ReentrantReadWriteLock.FairSync/NonfairSync
- Semaphore.Sync
2 AQS如何使用
2.1 相关概念
state:同步状态值,用于子类中实现状态记录;
acquire:修改state,可以理解为获取锁;
release:与acquire相反操作的修改state,可以理解为释放锁。
predecessor:前驱,successor:后继
Node.EXCLUSIVE:独占模式, Node.SHARED:共享模式
2.2 实现约定
使用AQS实现一个锁或同步器,需要依据以下几点去实现:
-
1 同步器需要以一个单独的数字表示状态。
-
2 同步器需要定义一个继承AQS的内部类去实现同步属性。
-
3 内部类继承AQS后,必须根据需要实现
tryAcquire*
/tryRelease*
去改变state。 -
4 有exclusive mode(默认)和shared两种模式:
- exclusive mode:独占模式,其他线程尝试acquire不会成功;
- shared mode:共享模式,多个线程尝试acquire都会成功。
-
5 一般只支持其中一种模式即可,但有特例:ReadWriteLock同时支持两种模式。
-
6 AbstractQueuedSynchronizer.ConditionObject 可以在子类中作为Condition的实现使用。
2.3 使用步骤
2.3.1 独占锁
基本步骤如下:
- 1 创建内部类继承AbstractQueuedSynchronizer;
- 2 实现
tryAcquire
/tryRelease
; - 3 lock时调用
acquire
,unlock时调用release
; - 4
isLocked
时调用isHeldExclusively
; - 5
hasQueuedThreads
时调用hasQueuedThreads
。
官方示例如下:
class Mutex implements Lock, java.io.Serializable {
// Our internal helper class
private static class Sync extends AbstractQueuedSynchronizer {
// Reports whether in locked state
protected boolean isHeldExclusively() {
return getState() == 1;
}
// Acquires the lock if state is zero
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// Releases the lock by setting state to zero
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// Provides a Condition
Condition newCondition() { return new ConditionObject(); }
// Deserializes properly
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
// The sync object does all the hard work. We just forward to it.
private final Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public boolean tryLock() { return sync.tryAcquire(1); }
public void unlock() { sync.release(1); }
public Condition newCondition() { return sync.newCondition(); }
public boolean isLocked() { return sync.isHeldExclusively(); }
public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
复制代码
2.3.2 共享锁
基本步骤如下:
- 1 创建内部类继承AbstractQueuedSynchronizer;
- 2 实现
tryAcquireShared
/tryReleaseShared
; - 3 await时调用
acquireShared
,signal时调用releaseShared
。
官方示例如下:
class BooleanLatch {
private static class Sync extends AbstractQueuedSynchronizer {
boolean isSignalled() { return getState() != 0; }
protected int tryAcquireShared(int ignore) {
return isSignalled() ? 1 : -1;
}
protected boolean tryReleaseShared(int ignore) {
setState(1);
return true;
}
}
private final Sync sync = new Sync();
public boolean isSignalled() { return sync.isSignalled(); }
public void signal() { sync.releaseShared(1); }
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
}
复制代码
3 主要属性
FIFO队列,由Node节点组成的链表:
+------+ prev +-----+ +-----+ head | | <---- | | <---- | | tail +------+ +-----+ +-----+
3.1 head
private transient volatile Node head;
等待队列的队头,延迟初始化。除了初始化的时候,仅当调用方法setHead(Node node)
时才会修改。
如果head已经存在,保证waitStatus不会是CANCELLED状态。
相关方法:
setHead(Node node)
setHeadAndPropagate(Node node, int propagate)
compareAndSetHead(Node update)
3.2 tail
private transient volatile Node tail;
等待队列的队尾, 延迟初始化。
仅当调用方法enq(Node)
添加新的等待节点时才会修改。
相关方法:
enq(final Node node)
findNodeFromTail(Node node)
compareAndSetTail(Node expect, Node update)
3.3 state
private volatile int state;
同步状态。
相关方法:
getState()
setState(int newState)
compareAndSetState(int expect, int update)
4 Node
4.1 属性
4.1.1 waitStatus
volatile int waitStatus
:状态
- CANCELLED = 1 : 表示当前节点被取消。
当前节点被取消,比如线程park后timeout或interrupt。
当一个节点状态变为CANCELLED后,不会再变成其他状态,其线程也不会再次被阻塞。
- SIGNAL = -1 : 表示当前节点的 后续节点的线程 需要被唤醒运行(unpark)。
说明当前节点的 后续节点 是block状态(或即将是block状态),即其线程被park了。
所以当前节点在release或cancel时,必须unpark它的后续节点。
为了避免竞争,acquire方法必须先表明需要一个信号,然后再尝试acquire,失败则阻塞。
- CONDITION = -2 :
表示当前节点的线程在condition队列中(ConditionObject类中设置),该节点直到其状态被设置为0时,才会转变成为同步队列中的节点。
- PROPAGATE = -3 : 当前场景下后续的acquireShared将会无条件被传播。
releaseShared(共享式的释放同步状态)需要被传播给其他节点;该状态在 doReleaseShared方法中被设置(仅仅适用于头节点)以保证传播,即使其它操作介入也不会中断。
- 0 : 当前节点在同步队列中,等待acquire。
waitStatus在同步队列中默认是0,condition队列中默认是CONDITION(-2)。
4.1.2 prev
volatile Node prev
:前驱节点,只对非ConditionObject外的节点定义启用。
相关方法:
Node predecessor()
:返回当前节点的前置节点;boolean hasQueuedPredecessors()
:当前节点(当前线程所在的节点)是否有前置节点。
4.1.3 next
volatile Node next
:后继节点;只对非ConditionObject外的节点定义启用,所以next的waitStatus不会是CONDITION。
相关方法:
void unparkSuccessor(Node node)
:uppark节点node的后继节点。
4.1.4 thread
volatile Thread thread
:当前节点的线程,Initialized on construction and nulled out after use.
4.1.5 nextWaiter
Node nextWaiter
:condition队列中的后继节点,只对ConditionObject内的节点定义启用,即当waitStatus==CONDITION时,才有值。
4.2 同步队列&Condition队列中Node的区别
比较 | 同步队列 | Condition队列 |
---|---|---|
使用的字段 | waitStatus/thread/prev/next | waitStatus/thread/nextWaiter |
waitStatus初始值 | 0 | CONDITION |
waitStatus可选值 | CANCELLED/SIGNAL/PROPAGATE/0 | CANCELLED/CONDITION |
5 AQS子类的实现约定:
- 根据需要,实现
tryAcquire*/tryRelease*
方法; - 使用
getState/setState(int)/compareAndSetState(int,int)
去监视或修改同步状态。
5.1 tryAcquire(int)
独占模式下尝试acquire。
这个方法实现时,需要查询当前状态是否允许acquire,然后再使用compareAndSetState进行acquire。
这个方法在acquire(int arg)
中被调用。如果调用失败,并且调用线程没在队列中,acquire方法会将该线程放入队列,直到被其他线程执行release释放。通常用来实现Lock.tryLock()。
返回值:true表示执行成功(state更新成功,成功acquire)。
5.2 tryRelease(int)
独占模式下,尝试修改state进行release。
返回值:true表示当前对象是一个完全release的状态,其他任何等待线程都可以尝试acquire。
5.3 tryAcquireShared(int)
共享模式下尝试acquire。
返回值:
- 负数:获取失败;
- 0 : 获取成功,但接下来共享模式下的请求不会成功,即没有剩余资源可用;
- 正数:获取成功,接下来共享模式下的acquire仍有可能成功, 即仍有剩余资源可用。 剩下的的等待线程必须检测对象的状态是否允许acquire。
5.4 tryReleaseShared(int)
共享模式下,尝试修改state进行release。
返回值:true表示share mode下release成功,即允许一个等待的acquire(shared or exclusive)成功进行。
5.5 isHeldExclusively()
当前线程是否以独占方式进行了同步(即状态被占用),也就是说,当前线程是否在独占资源。
这个方法在ConditionObject的方法中被调用,所以只有用到condition才需要去实现。
以上5个方法,默认的实现都是抛出 UnsupportedOperationException.
6 核心方法
6.1 addWaiter
过程:
- 1 使用当前线程和给定模式创建节点;
- 2 新创建的节点加在队尾,成为新的tail;
- 3 返回创建的新节点。
参数mode:Node.EXCLUSIVE 或 Node.SHARED
private Node addWaiter(Node mode) {
// 创建节点
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
// tail不为null,处理流程和enq(Node)中tail!=null时的处理流程一样。
node.prev = pred; //当前节点的前驱指向现在的tail
if (compareAndSetTail(pred, node)) { //将tail对象由原来的tail修改为当前新节点
pred.next = node; //原来的tail的next指向当前节点
return node;
}
}
// tail为null则调用enq方法。
enq(node);
return node;
}
复制代码
6.2 enq
过程:
- 1 先判断tail,如果tail是null:初始化head,并将tail指向head同一个Node;然后重新循环;
- 2 将新节点(参数node)加入到队尾;
- 3 返回原来的tail(即新节点的前置节点)。
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
* 插入新节点,需要的话初始化;参数是新增的节点,返回值是这个节点的前驱(即原来的tail)。
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
// tail为null,初始化tail/head。
if (compareAndSetHead(new Node()))
tail = head; //tail指向head
} else {
node.prev = t; //当前节点prev置指向原来的tail
if (compareAndSetTail(t, node)) { // 将tail对象由原来的tail修改为当前新节点
t.next = node; //原来的tail的next指向当前节点
return t;
}
}
}
}
复制代码
这里有个疑问,如果tail是null,但是head不是null,则tail不是一直不会被初始化了吗?
初始化head的方法是compareAndSetHead
,而根据compareAndSetHead
里的注释,只在enq
中调用:
/**
* CAS head field. Used only by enq.
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
复制代码
所以:head和tail,是在enq
方法中同时初始化的。
6.3 shouldParkAfterFailedAcquire
acquire失败后,判断node是否应该block。
过程:
如果node的前置pred的waitStatus == Node.SIGNAL,那么node应该block;否则做以下处理:
- 1 如果
pred.waitStatus == Node.CANCELLED
:说明前置节点已经取消。持续向前查询,直到找到一个waitStatus <= 0
的节点,作为node的新前置,并把CANCELLED的几点从队列中取消; - 2 如果pred.waitStatus是0或PROPAGATE(不会是CONDITION):因为0是初始状态,PROPAGATE是传播状态,所以前置如果是这两种状态,都应该将前置的状态修改为SIGNAL。但不应该park。
参数pred必须是参数node的前置。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 前驱节点的状态
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
// 当前节点(即pred的下一个节点)已经是等待状态,需要一个release去唤醒它。所以node可以安全的park.
return true;
if (ws > 0) { // CANCELLED
/*
waitStatus > 0 说明是canceled状态。
前驱是canceled状态,则表明前驱节点已经超时或者被中断,需要从同步队列中取消。
持续向前找,直到找到waitStatus<=0的节点,作为node的前驱.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
前驱节点waitStatus为 0 或 PROPAGATE,置换为SIGNAL.
不会是CONDITION,因为CONDITION用在ConditionObject中。
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
复制代码
6.4 unparkSuccessor
唤醒当前节点的后续节点。
过程:
- 1 如果节点不是CANCELLED,则将waitStatus设置为0;
- 2 如果后续节点是null或是CANCELLED状态,则对node之后第一个满足条件(waitStatus <= 0)的节点unpark。
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // waitStatus设置为0
Node s = node.next;
if (s == null || s.waitStatus > 0) {
/*
node的后继节点是null或是CANCELLED状态,释放该后继节点;
并从tail开始向前找,找到离node最近的一个非CANCELLED(waitStatus <= 0)的节点,作为要unpark的节点。
*/
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 并unpark后继节点。
if (s != null)
LockSupport.unpark(s.thread);
}
复制代码
6.5 acquireQueued
独占模式下,已经在队列中的线程进行acquire。用在acquire方法或者condition wait方法中。
过程:
- 1 只有node的前驱是head时,才轮到node尝试获取锁;
- 2 否则检查node是否应该park(node的前置节点waitStatus是不是等于SIGNAL),是的话则park;然后等待其他线程对当前线程unpark。
返回是否中断(park)。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
/*
只有前驱节点是head时,才轮到该节点争夺锁。
当前节点的前驱是head 并且 tryAcquire成功:
把当前要获取的节点设置为head,
*/
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
/*
如果当前节点不是head 或 tryAcquire失败,则检验当前节点的线程(此时即当前线程)是否应park。
parkAndCheckInterrupt会park当前线程,因此在这里block;将来有其他线程对当前线程unpark时,将继续此循环。
否则继续循环尝试acquire。
*/
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
复制代码
6.6 isOnSyncQueue
主要用于Condition流程中的判断,返回节点是否在同步队列中:
// Internal support methods for Conditions
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
// waitStatus是CONDITION,或prev是null
return false;
if (node.next != null)
// 根据文章前面对Node的描述,next只会在同步队列中有值,nextWaiter只会在condition队列中有值。
return true;
/*
因为CAS操作替换值的时候可能会失败,所以有可能出现:
node.prev不为null,但是没在同步队列中。
所以需要从队尾向前再遍历一遍。
return findNodeFromTail(node);
}
/**
从同步队列的tail开始向前遍历,查找是否有节点node。
*/
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
复制代码
7 独占模式同步
独占模式,需要子类的内部类,实现tryAcquire
/tryRelease
,并在lock和unlock时调用acquire/release
,如ReentrantLock.FairSync
。
7.1 acquire
- 1 tryAcquire(arg) 是否成功
- 2 不成功就调用addWaiter创建Node,并调用acquireQueued加入节点
- 3 acquireQueued成功的话就selfInterrupt.
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
复制代码
7.2 release
- 1 执行
tryRelease
,成功则继续执行,失败则返回false; - 2 head不为空且
nextWaiter!=0
(0是同步队列中的节点的nextWaiter的初始值),则执行unparkSuccessor
唤醒后继者,然后返回true。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//unpark节点head的后继者
return true;
}
return false;
}
复制代码
8 共享模式同步
共享模式,需要子类的内部类,实现tryAcquireShared
/tryReleaseShared
,并在lock和unlock时调用acquireShared/tryReleaseShared
,如CountDownLatch.Sync
。
8.1 acquireShared
tryAcquireShared失败,则执行doAcquireShared。
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
复制代码
private void doAcquireShared(int arg) {
// 创建Node.SHARED模式的新节点(添加在队尾)
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
// 循环
for (;;) {
/*
1 获取当前节点的前置节点;
2 前置节点是head,则tryAcquireShared,如果成功,则将当前节点设置为head,并将结果向后传播。
*/
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
//设置head值,并将tryAcquireShared结果向后传播
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
// 前置节点不是head,当前线程是否park;
// 如果park当前线程,将来有其他线程对当前线程unpark时,将继续此循环。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
复制代码
8.2 releaseShared
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
复制代码