什么是AQS
用来构建锁或其他同步器组件的重量级基础框架及整个 JUC体系的基石,通过内置的 CLH队列的变种来完成资源获取线程的排队工作,将每条要去抢占资源的线程封成一Node节点来实现锁的分配,有一个 int 的 state 变量表示持有锁的状态,通过 CAS 完成对 state 变量的修改 0表示没有,1表示阻塞, 大于1表示可重入锁
AQS 内部体系架构
AQS 内部 state 变量就是代表是否阻塞 0 表示没有人使用,可以获取锁, 1 表示有人在使用,阻塞中, 大于1 是可重入锁
内部类 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唤醒 */
static final int CONDITION = -2;
/** 共享式同步状态获取将会无条件传播下去 */
static final int PROPAGATE = -3;
/** 初始为0, 状态为上面的几种 */
volatile int waitStatus;
/** 前置节点 */
volatile Node prev;
/** 后置节点 */
volatile Node next;
属性说明
AQS 同步队列基本结构
源码解析
从常用的 ReentrantLock 开始解读,假设有三个线程,分别代表三个人在银行办理业务,但是只有一个业务窗口
创建 ReentrantLock , 有公平锁和非公平锁.默认创建非公平锁
公平锁与非公平锁的lock()方法唯一的区别就在于公平锁在获取同步状态时多了一个限制条件
限制条件 hasQueuedPredecessors() 其他方法一致
hasQueuedPredecessors() 是公平锁加锁时判断等待队列中是否存在有效节点的方法
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
本次以非公平锁开始分析
抢锁
假设A线程直接抢占成功,BC线程则进行后续抢占,进入 acquire(int arg) 方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
进入第一个判断方法后发现父类使用模板模式,要求子类必须重写该方法,不然直接抛异常
进入非公平锁重写的方法发现直接调用了 nonfairTryAcquire(int acquires) 方法
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取state状态
int c = getState();
if (c == 0) {
//比较并交换,将state改为1
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;
}
此时线程B执行完后会返回false
在 acquire 方法的判断内, !tryAcquire(arg) = true
所以继续执行后面的 addWaiter(Node.EXCLUSIVE) , 参数 Node.EXCLUSIVE = null
private Node addWaiter(Node mode) {
//创建一个node节点,线程为B, waitState为0
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//此时 tail 尾结点 为null
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//直接执行该方法创建队列
enq(node);
return node;
}
private Node enq(final Node node) {
//自旋操作
for (;;) {
//第一次 tail 为null
//第二次 tail = head
Node t = tail;
if (t == null) { // Must initialize
//创建一个空的哨兵结点(占位用), thread = null, 并将 head指向该节点
if (compareAndSetHead(new Node()))
//将尾节点指向头结点
tail = head;
} else {
//将B节点的前节点指向 head 节点
node.prev = t;
//通过CAS将 tail 指向 B 节点
if (compareAndSetTail(t, node)) {
//将哨兵节点的下一节点指向B节点
t.next = node;
return t;
}
}
}
}
执行完后队列结构
同理,C线程进入队列,tail尾结点指向C节点,B节点next指向C, C节点 prev指向B,最后队列结构
返回 B节点或C节点后继续进行判断
final boolean acquireQueued(final Node node, int arg) {
//取消排队,可能线程不愿意等待了
boolean failed = true;
try {
boolean interrupted = false;
//自旋操作
for (;;) {
// 假设该 Node 为B, 则 p 为head哨兵节点
final Node p = node.predecessor();
//由于A线程没有释放锁
//tryAcquire(arg) 会跟上面一样继续返回 false,没有抢占到锁
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//第一次shouldParkAfterFailedAcquire方法将哨兵节点waitState=-1
//第二次shouldParkAfterFailedAcquire方法返回true,继续执行
//执行parkAndCheckInterrupt()方法时直接阻塞线程,不再执行后续操作
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private final boolean parkAndCheckInterrupt() {
//线程阻塞,等待获取通行证
LockSupport.park(this);
//在调用unpark方法后再向下执行
return Thread.interrupted();
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 第一次为 0
// 第二次为 -1
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) //Node.SIGNAL = -1
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
//第二次直接返回true
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
// 将哨兵节点的 waitState 赋值为 -1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
此时 B和 C线程都将阻塞在 parkAndCheckInterrupt() 方法内,等待A线程执行完释放锁唤醒其他线程
解锁
A线程执行完毕,调用unlock()方法
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//判断是否完全释放锁,并重置状态
if (tryRelease(arg)) {
Node h = head;
//哨兵节点的 waitState 此时为 -1
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
//假设只获取了一次锁,则 c = 0
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
//将当前占用线程设为null
setExclusiveOwnerThread(null);
}
//设置锁占用情况
setState(c);
return free;
}
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus; // -1
if (ws < 0)
//将哨兵节点的waitState改为0
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
//B 节点, waitState 为 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)
//解锁线程 B C, 获取到通行证
LockSupport.unpark(s.thread);
}
回到前面线程抢锁阻塞处
在 A线程执行完unlock()方法后, 在前面说的 B 和 C 线程阻塞处获取到通行证,继续执行返回 true
final boolean acquireQueued(final Node node, int arg) {
//取消排队,可能线程不愿意等待了
boolean failed = true;
try {
boolean interrupted = false;
//自旋操作
for (;;) {
final Node p = node.predecessor();
//此时 state 为 0 ,tryAcquire(arg)方法中进行抢占锁
//成功抢占的将占用线程设为当前线程,并修改 state为 1
if (p == head && tryAcquire(arg)) {
//设置哨兵节点为当前节点,当前节点的线程和前节点设为null,变为新的哨兵节点
setHead(node);
//并将之前的哨兵节点下一节点设为null帮助GC
p.next = null; // help GC
failed = false;
return interrupted;
}
//没抢占到的线程继续在parkAndCheckInterrupt方法等待通行证
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//获取通行证后继续执行
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
假设B 线程抢占成功,则判断成立,执行 selfInterrupt() 方法处理线程 B 业务
最后队列结构为
阅读源码需要一步一步走, 一定要自己画流程图理解,这样才能更加深入的理解整体流程