文章目录
什么是AQS
全称 AbstractQueuedSynchronizer,意为抽象的队列同步器
顾名思义它是一个抽象类,内部存在一个同步队列,在存在并发安全的地方使多线程同步执行
AQS做了什么
AQS 的实现使用模板方法设计模式,其内设计了线程从获取锁到到释放锁的整个流程,并实现了获取失败加入队列、释放锁后唤醒等待线程以及条件等待 condition 等大部分功能,子类只需实现其预留的钩子方法即可,像java中的重入锁 ReentrantLock、读写锁 ReentrantReadWriteLock 以及常用的并发工具类 CountDownLatch、Semaphore 等都是利用AQS实现的
AQS是如何保证并发安全的
AQS 通过volatile 加 上 CAS 来保证并发安全
volatile
volatile 修饰的变量在被线程修改后会立即刷新到主内存中,并使其他线程对该变量的缓存失效,使其重新从主内存中读取,这样就解决了多线程并发可见性问题
volatile 通过设置内存屏障来禁止JVM指令重排序, 从而解决多线程并发有序性
CAS
Compare and Swap,即比较再交换
线程会把变量在主内存中的值拷贝到自己的工作空间,更新这个变量时,会比较工作空间的值是否与主内存中的值一样,一样则更新,否则不更新。这样能解决多线程并发的原子性问题
它通过volatile(解决可见性和有序性问题)加 CAS(解决原子性问题)来保证并发安全
父类 AbstractOwnableSynchronizer
这个类的实现很简单,就是提供设置(获取)持有独占锁线程方法
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
主要成员变量
private volatile int state;//锁计数器,为0表示无锁状态
private transient volatile Node head; // CLH对列对头
private transient volatile Node tail; // CLH队列队尾
CLH队列
一个虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系,下文中的同步队列指的就是这个队列
静态内部类Node
Node是同步队列的结点类,主要成员变量如下:
volatile int waitStatus; //等待状态, 下文中的ws就是指这个变量
volatile Node prev; //前驱结点
volatile Node next; //后继结点
volatile Thread thread; //等待的线程
Node nextWaiter; //使用condition时构成的单向条件队列,指向下一个等待结点
除了 nextWaiter 其他变量都用 volatile 修饰保证多线程环境下可见性,而每个线程结点的 nextWaiter 都是不一样的,相当于线程的局部变量,所以不需要 volatile 修饰
waitStatus取值常量
static final int CANCELLED = 1; //表示线程已经被取消,这种结点会被移出队列
static final int SIGNAL = -1; //表示当前线程释放锁后需要唤醒后继节点
static final int CONDITION = -2; //表示当前结点在等待一个condition条件队列中
static final int PROPAGATE = -3; //表示共享锁模式下,当前结点以传播的方式唤醒结点,(也就是唤醒的下个结点后还会继续唤醒其他结点)
waitStatus还有个取值为0,相当于默认值,AQS 中有多处会将waitStatus设置为0
锁类型常量
static final Node SHARED = new Node(); //共享锁模式
static final Node EXCLUSIVE = null; //独占锁模式
CAS操作相关的常量
private static final Unsafe unsafe = Unsafe.getUnsafe(); //提供获取变量地址偏移量和cas操作方法的工具类,其内部都是一些native方法
private static final long stateOffset; //成员变量state的地址偏移量
private static final long headOffset; //成员变量head的地址偏移量
private static final long tailOffset; //成员变量tail的地址偏移量
private static final long waitStatusOffset; //成员变量waitStatus的地址偏移量
private static final long nextOffset; //成员变量next的地址偏移量
//这些偏移地址常量在对变量进行CAS操作时使用,通过静态代码块进行初始化
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); }
}
CAS操作相关的方法
/**
* @param expect 期望值
* @param update 更新值
*/
protected final boolean compareAndSetState(int expect, int update) {
//stateOffset state的地址偏移量
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
类似的方法还有compareAndSetHead、compareAndSetTail、compareAndSetWaitStatus、compareAndSetNext
独占锁的获取与释放流程
获取独占锁 acquire()
/**
* @param arg 获取锁,计数器state需要加arg,一般取值为1
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
/**
* 尝试获取锁失败,则执行acquireQueued()加入队列,并将当前线程挂起
* 线程重新唤醒后acquireQueued()返回线程是否被标记中断,若是则中断线程
*/
selfInterrupt();
}
tryAcquire()
空实现,留给子类实现的钩子方法,真正获取锁的逻辑就在这个方法中
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
下面是 ReentrantLock 非公平锁的实现,主要逻辑是判断state为0或是当前线程持有独占锁,则以 CAS 方式给state加上acquires(一般都是1)成功则获取锁,否则获取锁失败
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
` final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
addWaiter()
线程加入同步队列
/**
* @param mode 锁类型,对应类型常量SHARED(共享锁)、EXCLUSIVE(独占锁)
*/
private Node addWaiter(Node mode) {
//构造线程结点
Node node = new Node(Thread.currentThread(), mode);
// 尝试快速将结点入队
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//快速入队失败,执行enq()入队,失败会不断重试直至成功
enq(node);
return node;
}
enq() 线程结点入队
private Node enq(final Node node) {
for (;;) { //操控失败会重试,直至成功才跳出循环
Node t = tail;
if (t == null) { //tail为null,head也一定为null,对列初始化
/**
* 这里先设置head再把head赋值给tail,而不是先设置tail再把tail赋值给head的原因.
* 假如执行compareAndSetHead后如果当前线程A失去cpu执行权,线程B再进入此处执行compareAndSetHead,
* 因为compareAndSetHead方法中设置head时,期望值为null,而线程A已经执行compareAndSetHead给head设值了,
* 所以线程B执行compareAndSetHead会一直失败,直到线程A重新获得cpu执行权给tail赋值,
* 再轮到线程B执行时,tail不为null直接执行else处代码,这样防止了多个线程给head和tail设值而导致出现线程安全问题
*/
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
private final boolean compareAndSetHead(Node update) {
//期望值为null
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
acquireQueued()
挂起线程等待,唤醒后重新去获取锁
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
//获取锁成功,head结点出队,当前结点变为head
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return interrupted;
}
//获取锁失败,挂起线程,等待重新唤醒后再执行后续代码
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire()
若前驱结点的ws为SIGNAL返回true,
若为CANCELLED,则清理同步队列中ws为CANCELLE结点,在acquireQueued()则会重新获取node的前驱结点再继续调用该方法
若ws 为其他值则将ws设为SIGNAL
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
// 将ws为CANCELLED移出队列
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
parkAndCheckInterrupt() 挂起线程
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); // 内部调用了Unsafe的native方法
//线程再次唤醒后,返回线程是否被中断
return Thread.interrupted();
}
释放独占锁release()
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
//释放锁成功且head不为0则唤醒后继结点
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease()
释放锁的主要逻辑,交给子类实现的钩子方法
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
下面是 ReentrantLock中的实现,主要逻辑:持有锁的线程为当前线程时,将state的值减去releases
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 持有独占锁的线程只有一个,这段代码时单线程执行的,不需要使用cas的方式设置state
setState(c);
return free;
}
unparkSuccessor()
唤醒后继结点线程
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
//后继结点为空或者ws为CANCELLED,从tail开始寻找到一个不为null且ws不为CANCELLED的结点
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); //唤醒线程,内部调用UNSAFE类的本地方法
}
共享锁的获取与释放流程
获取共享锁acquireShared()
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
// 获取锁失败加入同步队列
doAcquireShared(arg);
}
tryAcquireShared()
获取锁的主要逻辑方法,也是交给子类实现的钩子方法
/**
* 返回负数表示获取锁失败,
* 返回0表示成功且没有线程在等待获取共享锁
* 返回正数也表示成功但有线程在等待获取共享锁
*/
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
下面是 ReentrantReadWriteLock中的实现,
主要逻辑是判断独占锁有没有被其他线程持有,
若没有,则通过CAS操作修改state成功则获取到锁,失败也会进入循环中不断重试
若有,则获取锁失败
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current) // 独占锁被其他线程持有不能获取共享锁直接失败,
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() && // 是否应该阻塞,对于公平锁来说前面有线程等待则应该被阻塞
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {// cas操作成功则获取到锁
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1; // 获取锁成功返回正数1
}
/**
* 进入循环中重试,
* 只要独占锁没有被持,共享锁可以多个线程持有的,
* 上面获锁失败可能是cas操作失败(其他获取共享锁的线程也会并发修改state)
* 所以要进入循环中重试
*/
return fullTryAcquireShared(current);
}
doAcquireShared()
加入同步队列中等待,并将线程挂起
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED); //构建一个当前结点共享模式的node
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
// 成功获取共享锁
if (r >= 0) {
//设置当前结点为head并并以传播的方式唤醒等待共享锁的线程
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) // 设置前驱结点ws为SIGNAL并挂起线程
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
setHeadAndPropagate()
将当前结点设置为head,如果后继结点在等待共享锁则需要将其唤醒
获取共享锁的线程之间是不冲突的,一个线程获取到共享锁,其他等待的线程也应被唤醒获取共享锁
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
setHead(node);
/**
* propagate>0说明有线程等待共享锁
* 老的head或当前最新的head的ws不为CANCELLED(小于0就是不为CANCELLED)
* 这些情况都说明有线程在等待锁,
* 这时获取node的后继结点如果为共享锁模式则唤醒后继结点
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared(); // 唤醒队列中等待共享锁的线程,这个方法在释放锁处会详细讲到
}
释放共享锁releaseShared()
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared(); //释放锁成功,唤醒等待线程
return true;
}
return false;
}
tryReleaseShared()
释放锁的主要逻辑方法,交给子类实现的钩子方法
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
下面是 ReentrantReadWriteLock中的实现,
主要逻辑也是修改state值,但与与独占锁不同,这里存在多线程并发,所以要以CAS方式修改,并且失败需要不断重试
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc)) // cas方式修改state
return nextc == 0;
}
}
doReleaseShared()
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) // 这里cas操作成功后再去唤醒后继线程防止多个线程同时唤醒同一个线程
continue;
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
/**
* ws为0说明h的后继结点已经或正在被唤醒,这时将h标记为PROPAGATE状态
* cas操作失败说明,第一个判断后线程失去执行权,
* 其他线程已经将h的ws设为了PROPAGATE,
* 说明这时多线程竞争激烈(有很多等待共享锁的线程被唤醒,也就可能还有很多线程没被唤醒),
* 这时当前线程就继续进入循环与其他线程一起唤醒等待的线程
*/
continue;
}
/**
* head结点改变,说明在当前线程执行一次循环中,有其他线程唤醒了等待线程并重置了head,
* 这时同样说明多线程竞争激烈,当前线程就继续进入循环来唤醒其他线程
* 同时这也是退出循环的标志,因为head的后继结点为最后一个要唤醒的结点时,
* 其他线程就没有结点要唤醒了,这是head就不会被其他线程重置,h也就一定等于head
* 当然h等于head并不说明一定是最后一个结点被唤醒,但说明多线程竞争并不激烈,也就不需要帮助其他线程一起唤醒等待的线程
*/
if (h == head) //head改变,继续进入循环继续队列中的线程
break;
}
唤醒等待共享锁线程是并发进行的,因为不止释放共享锁时会唤醒等待的线程,线程在被唤醒并获取到共享锁后,也会唤醒其他等待共享锁的线程,而被唤醒的线程获取到锁后也会加入到这个队伍去唤醒其他等待的线程,这样即使有大量等待线程,也会很快被全部唤醒
内部类 ConditionObject
ConditionObject类AQS是实现Condition功能的一个内部类
Condition实现主要逻辑
await()会将当前线程A加入条件队列,然后释放锁并挂起线程A,
当其他线程B调用signal()和signalAll()并不会将挂起的线程A唤醒,而是将线程A移出条件队列并移入同步队列,
等到持有锁的线程C释放锁后才会被唤醒,唤醒后的线程A还要去获取锁若不成功的话还要被挂起
成员变量
/** 条件队列中的第一个等待的结点 */
private transient Node firstWaiter;
/** 条件队列中的最后一个等待的结点 */
private transient Node lastWaiter;
线程中断模式常量
/** 调用thread interrupt()中断 */
private static final int REINTERRUPT = 1;
/** 抛异常中断 throw new InterruptedException() */
private static final int THROW_IE = -1;
核心方法
await() 加入条件等待
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter(); //线程加入条件队列
int savedState = fullyRelease(node); //释放锁资源并返回state在释放锁之前的值
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
/**
* 当前线程不在队列中,挂起线程
* 当其他线程调用signal通知到了该线程会将这个线程加入同步队列中
* 再轮到该线程执行时就可以跳出循环
*/
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);
}
addConditionWaiter() 加入条件队列
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters(); //这里将条件队列中所有ws不为CONDITION的结点清除
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
fullyRelease() 释放锁资源
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
isOnSyncQueue() 结点是否在同步队列中
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
return findNodeFromTail(node); //在同步队列中查找存在node结点
}
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) { //从后往前遍历寻找是否存在node
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
signal()通知线程结束等待
public final void signal() {
if (!isHeldExclusively()) //当前线程是否占有独占锁
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first); //通知条件队列中第一个等待线程
}
doSignal()
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null) //这里将first移出条件队列
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) && //将first加入同步队列
(first = firstWaiter) != null);
}
signalAll()
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first); //条件队列所有结点全部出队
}
doSignalAll()
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}