AQS
抽象基础类AbstractQueuedSynchronizer(抽象同步器类,简称为AQS),用于解决CAS恶性空自旋和总线风暴问题的双向链表。内部为虚拟队列,CLH队列的变种,AQS中还有一个条件队列,使用单向链表,用于替代Object的wait()和notify(),当调用Condition的await()时,节点从同步队列中移除并加入到ConditionObject的的队列中,当调用signal()时,重新加入到同步队列中
ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock、FutureTask都是基于AQS,AQS使用模版模式,定义了模版方法和钩子方法,模版方法定义了业务的操作框架,钩子方法由子类实现,被模版方法调用
模版方法:public final void acquire(int arg)、boolean release(int arg)
钩子方法:boolean tryAcquire(int arg)、boolean tryRelease(int arg)
钩子方法中默认抛出异常
原理
JDK8
以ReentrantLock为例,内部的Sync实现了AbstractQueuedSynchronizer,
内部类Node中的标识:
抢占锁的模式,字段nextWaiter:
static final Node SHARED = new Node();表示当前节点为共享模式static final Node EXCLUSIVE = null;表示当前节点为独占模式
等待状态,字段waitStatus:
static final int CANCELLED = 1;取消状态static final int SIGNAL = -1;后置节点等待唤醒static final int CONDITION = -2;条件等待static final int PROPAGATE = -3;共享锁等待
ReentrantLock获取锁和释放锁时,都是通过调用Sync的方法实现
-
公平锁:队首节点即占用锁的节点
-
抢锁
ReentrantLock调用方法lock()获取锁,等价于调用内部类Sync(实现了AbstractQueuedSynchronizer的抽象类,具体实现类为FairSync、NonfairSync)的lock()方法,Sync调用父类的模版方法acquire(1)将锁的状态加1acquire()方法中先通过子类实现的钩子方法tryAcquire()尝试获取锁,成功则直接返回,失败则通过addWaiter()先将当前线程封装为一个新Node,模式设为Node.EXCLUSIVE,通过CAS添加到队尾,若队列未初始化/有锁的竞争,则通过enq()方法使用CAS自旋加入队列尾部- 加入队列后调用
acquireQueued(),该方法会阻塞线程防止空自旋:开始自旋抢锁,第一次进入自旋,前置节点若是队首节点则尝试抢锁,抢锁失败调用shouldParkAfterFailedAcquire(),若前置节点的waitStatus是SIGNAL,则调用LockSupport.park()阻塞线程,直到前直接点释放锁唤醒当前线程,若前置节点的waitStatus是CANCELLED则更新当前节点的前置节点为有效的节点,若是其余情况则通过CAS设置前置节点的waitStatus为SINGAL,第二次再进入循环时返回true阻塞线程 - 锁被释放时唤醒该节点的后置节点的线程让其抢锁
-
释放锁:
-
-
非公平锁:非公平性主要体现在若锁刚被释放,新来的线程直接尝试抢占锁
public class ReentrantLock implements Lock, java.io.Serializable {
//继承AQS的抽象类,该类的子类实现了公平锁和非公平锁
private final Sync sync;
//获取锁
public void lock() {
sync.lock();
}
//释放锁
public void unlock() {
sync.release(1);
}
}
FairSync公平锁
static final class FairSync extends Sync {
//获取公平锁
final void lock() {
acquire(1);//调用模版方法
}
//钩子方法,由具体的子类实现,尝试获取锁
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();//获取锁的状态
if (c == 0) {//未上锁
if (!hasQueuedPredecessors() &&//队里中若有线程在等待获取锁
compareAndSetState(0, acquires)) {//可能存在竞争,使用CAS保证原子操作
setExclusiveOwnerThread(current);//成功后设置独占锁的线程为当前线程
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//已上锁且获取锁的线程是当前线程,可重入
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);//不是用compareAndSetState(),因为累加的线程只会是同一个线程,不存在竞争关系
return true;
}
return false;
}
}
NonfairSync非公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))//直接开始抢锁,忽视等待队列中是否有等待的节点
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);//抢锁失败开始
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);//该方法是父类Sync中的,主要是忽略等待队列中是否有等待线程直接尝试抢锁
}
}
释放锁,钩子方法的实现在抽象类Sync中
abstract static class Sync extends AbstractQueuedSynchronizer {
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)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
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);
}
setState(c);//更新锁状态
return free;
}
}
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
//锁的状态,初始为0,获取锁成功时加1,可重入锁,释放时减1
private volatile int state;
//队列的头节点,值通过setHead()方法修改,头节点为当前获取锁的节点
private transient volatile Node head;
//队列的尾节点,只能通过enq()方法修改
private transient volatile Node tail;
//获取锁的模版方法
public final void acquire(int arg) {
if (!tryAcquire(arg) && //尝试获取锁,成功则直接返回
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//获取锁失败时addWaiter()将独占模式节点添加到队尾
selfInterrupt();//线程唤醒后
}
//释放锁的模版方法
public final boolean release(int arg) {
if (tryRelease(arg)) {//释放锁成功
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
//将节点添加到队列的队尾
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)) {//通过CAS原子操作尝试将队尾节点替换为新节点
pred.next = node;//成功后将旧的尾节点的next指向新的尾节点
return node;
}
}
//添加失败,有锁的竞争/未初始化
enq(node);
return node;
}
//节点自旋入队,队尾
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // 初始化头、尾节点为新节点
if (compareAndSetHead(new Node()))//compareAndSetHead()方法仅被enq()方法调用
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {//通过CAS将尾节点换为新节点
t.next = node;//抢锁成功后
return t;
}
}
}
}
//自旋抢占锁
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();//新节点的前置节点
if (p == head && tryAcquire(arg)) {//当该节点为前置节点为队首节点时,则尝试获取锁,保证先进先出(FIFO)
setHead(node);//抢占成功后,移除前置节点,将队首节点设为当前节点
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&//第一次执行时将前置节点的waitStatus设置为Signal,第二次进入时阻塞节点所在线程放置空自旋,等待队首节点释放锁时唤醒
parkAndCheckInterrupt())//通过LockSupport.park()阻塞线程,唤醒后是线程是否被中断
interrupted = true;
}
} finally {
if (failed)//超时或被中断取消节点在队里中的等待并移除
cancelAcquire(node);
}
}
//当前节点的线程是否需要阻塞等待
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)//前置节点
return true;
if (ws > 0) {//前置节点被取消不再等待,循环查找有效的前置节点并重新设置当前节点的有效前置节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);//将前置节点设置为SIGNAL状态
}
return false;
}
//查询队列中是否有线程比当前线程等待获取时间更长,主要用于公平锁
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&//tail、head未初始化时都为null或刚初始化,说明没有抢锁的竞争
((s = h.next) == null || s.thread != Thread.currentThread());//h.next为null时可能是enq()中刚入队尾的新节点if (compareAndSetTail(t, node))还未给旧的尾节点添加next
}
//释放锁成功后唤醒下一节点
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)//重置释放锁节点的waitStatus为初始状态
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {//后置节点若被取消则从队列尾部向前查找有效节点
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);
}
}
JDK17
NonfairSync、FairSync与在JDK中的差别不大,Sync提炼抽象方法initialTryLock()供子类实现,该方法在获取锁时调用,实现了锁的可重入,获取锁失败时调用AbstractQueuedSynchronizer的acquire(1)
Node中的标识:
-
static final int WAITING = 1;后置节点等待被唤醒 -
static final int CANCELLED = 0x80000000;取消状态 -
static final int COND = 2;条件等待独占、共享、条件用子类表示
ExclusiveNode、SharedNode、ConditionNode
抢锁:
- 先尝试获取锁,获取失败在
acquire()方法中自旋尝试获取锁 - 第一次进入循环因为
pred为null会尝试获取锁,成功后返回,失败后进入判断if (node == null)初始化当前节点为ExclusiveNode - 第二次循环也会尝试获取锁,失败后进入判断
else if (pred == null),设置当前节点的属性并尝试加入队列 - 第三次循环先判断当前节点的前置节点若不是队首节点,处理前置节点被取消的情况,否则进入判断
else if (node.status == 0)设置节点的status - 第四次循环与第三次类似,最后会进入判断
else,阻塞线程
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
public final void acquire(int arg) {
if (!tryAcquire(arg))//调用子类的实现尝试获取锁
acquire(null, arg, false, false, false, 0L);
}
final int acquire(Node node, int arg, boolean shared,
boolean interruptible, boolean timed, long time) {
Thread current = Thread.currentThread();
byte spins = 0, postSpins = 0;
boolean interrupted = false, first = false;//first表示当前节点的前置节点是否是队首节点
Node pred = null;//当前节点的前置节点
for (;;) {
if (!first && (pred = (node == null) ? null : node.prev) != null &&
!(first = (head == pred))) {//第三次循环时,若前置节点不是队首节点
if (pred.status < 0) {
cleanQueue();// 前置节点被取消
continue;
} else if (pred.prev == null) {
Thread.onSpinWait();
continue;
}
}
if (first || pred == null) {//前置节点为队首节点、第一次、第二次循环会进入
boolean acquired;
try {//尝试获取锁
if (shared)
acquired = (tryAcquireShared(arg) >= 0);
else
acquired = tryAcquire(arg);
} catch (Throwable ex) {
cancelAcquire(node, interrupted, false);
throw ex;
}
if (acquired) {//获取锁成功
if (first) {//前置节点为队首节点更新队首节点为当前节点
node.prev = null;
head = node;
pred.next = null;
node.waiter = null;
if (shared)
signalNextIfShared(node);
if (interrupted)
current.interrupt();
}
return 1;
}
}
//获取锁失败时进入判断
if (node == null) { //第一次循环,节点入队前初始化
if (shared)
node = new SharedNode();
else
node = new ExclusiveNode();
} else if (pred == null) { //第二次循环,尝试入队
node.waiter = current;
Node t = tail;
node.setPrevRelaxed(t); //设置当前节点的前置节点为旧尾节点
if (t == null)//尾节点未初始化
tryInitializeHead();
else if (!casTail(t, node))//通过CAS将尾节点设为当前节点
node.setPrevRelaxed(null); //设置失败时清除当前节点的前置节点
else
t.next = node;//尾节点设置成功时设置旧尾节点的后置节点为当前节点
} else if (first && spins != 0) {
--spins; // reduce unfairness on rewaits
Thread.onSpinWait();
} else if (node.status == 0) {//第三次循环
node.status = WAITING; // enable signal and recheck
} else {//阻塞线程,避免空自旋
long nanos;
spins = postSpins = (byte)((postSpins << 1) | 1);
if (!timed)
LockSupport.park(this);
else if ((nanos = time - System.nanoTime()) > 0L)
LockSupport.parkNanos(this, nanos);
else
break;
node.clearStatus();//线程唤醒时清除节点的status
if ((interrupted |= Thread.interrupted()) && interruptible)
break;
}
}
return cancelAcquire(node, interrupted, interruptible);
}
//清除队列中的取消节点,并重新设置节点的有效节点
private void cleanQueue() {
for (;;) {
for (Node q = tail, s = null, p, n;;) { // (p, q, s) p为q的前置节点,s为q的后置节点
if (q == null || (p = q.prev) == null)
return; // 队列结束
if (s == null ? tail != q : (s.prev != q || s.status < 0))
//后置节点为null若当前节点已不是尾节点,有竞争,结束循环;后置节点在循环过程中被取消,结束循环
break;
if (q.status < 0) {//若p取消状态,将p、s的后置、前置节点相互设置
// 当前节点被取消,且为尾节点,将p设为尾节点,不是尾节点,将q的后置节点的前置节点设为q的前置节点
if ((s == null ? casTail(q, p) : s.casPrev(q, p)) &&
q.prev == p) {//确保q的前置节点未更改
p.casNext(q, s); // p的后置节点设为s
if (p.prev == null)
signalNext(p);//唤醒p的后置节点
}
break;
}
if ((n = p.next) != q) {//防止并发链表指针丢失问题,确保一致性
if (n != null && q.prev == p) {
p.casNext(n, q);
if (p.prev == null)
signalNext(p);
}
break;
}
s = q;
q = q.prev;
}
}
}
}