AQS是Java中的AbstractQueueSynchronizer类,底层维护了一个双向链表存储未获取到资源的线程。
一.Sync类
静态内部类Sync继承了AQS类,AQS底层维护了一个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;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
// Node对象存储标识的地方
volatile int waitStatus;
// 前驱节点
volatile Node prev;
// 后继节点
volatile Node next;
// 当前Node绑定的线程
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;
}
}
二.加锁
1.从lock进入锁
首先找到加锁方法的入口,源码第284行进入
public void lock() {
// sync分为公平和非公平
sync.lock();
}
对于加锁,ReentranLock可以实现公平锁和非公平锁,也是互斥锁,可重入锁。
2.非公平锁
源码第205行
final void lock() {
// 通过CAS的方式将state从0修改为1,如果返回true代表修改成功,否则修改失败
if (compareAndSetState(0, 1))
// 将一个属性设置为当前线程,这个属性是AQS的父类提供,证明当前线程拿到了锁资源
setExclusiveOwnerThread(Thread.currentThread());
else
// 获取失败后执行的方法
acquire(1);
}
3.acquire方法
源码第1197行
public final void acquire(int arg) {
// 再次尝试获取锁资源,如果尝试成功直接返回true
if (!tryAcquire(arg) &&
// 获取锁失败后将当前线程追加为一个Node,追加到AQS的双向链表中
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 中断线程
selfInterrupt();
}
4.tryAcquire方法
源码第212行
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
源码第129行
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取AQS中的state值
int c = getState();
// 如果state为0,代表可以尝试获取锁资源
if (c == 0) {
// 尝试CAS修改state,如果成功返回ture,否则返回false
if (compareAndSetState(0, acquires)) {
// 设置属性为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
// 说明state不是0,判断当前占用锁资源的是否是当前线程,是则进行可重入操作
else if (current == getExclusiveOwnerThread()) {
// 将state+1
int nextc = c + acquires;
// 如果state+1后小于0,说明超过了锁可重入的最大值
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 没问题就对state重新赋值
setState(nextc);
// 锁重入成功
return true;
}
return false;
}
也就是说tryAcquire()返回true的情况为获取锁成功获取重入锁成功两种情况,如果没有获取锁成功,则进行acquireQueued方法的判断。
5.addWaiter
AQS源码第605行
// 说明获取锁失败,放到等待队列中等待
private Node addWaiter(Node mode) {
// 创建Node类并绑定当前线程,设置为排他锁
Node node = new Node(Thread.currentThread(), mode);
// 获取AQS队列的尾部节点
Node pred = tail;
// 如果队列中有其他节点在排队
if (pred != null) {
node.prev = pred;
// 基于CAS方式将当前节点设置为尾节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 走到这里说明没人排队或者前面CAS失败也会往队列尾部添加节点
enq(node);
return node;
}
AQS第583行
private Node enq(final Node node) {
// 死循环
for (;;) {
// 获取当前节点的尾节点
Node t = tail;
// 如果尾节点为空说明没人其他节点排队
if (t == null) {
// 通过CAS操作将当前节点设为尾节点,初始化一个空节点作为头节点,这个头节点没有意义
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 有节点排队,往队列尾部添加节点
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
6.acquireQueued方法
AQS源码第857行
// 已经将node加入到了双向队列中,然后执行当前方法
final boolean acquireQueued(final Node node, int arg) {
// 标识
boolean failed = true;
try {
// 标识
boolean interrupted = false;
// 死循环
for (;;) {
// 返回当前node的前驱节点,如果获取不到抛空指针异常
final Node p = node.predecessor();
// 如果前驱节点p是头节点,说明此时队列只有当前node节点,尝试再次获取锁资源(state从0-1,或者锁重入)
if (p == head && tryAcquire(arg)) {
// 走到这里说明获取锁成功,设置node节点为当前头节点,此时队列中无节点排队
setHead(node);
p.next = null; // help GC
// 将标识修改为false
failed = false;
return interrupted;
}
// 到这里说明没获取到锁资源,前驱节点是有效节点才会返回true,才会将线程阻塞,等待唤醒
if (shouldParkAfterFailedAcquire(p, node) &&
// 基于UnSafe类的park()方法挂起线程,唯一可能出现异常的地方
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 执行的机率为0,除非JVM内部出现问题
if (failed)
cancelAcquire(node);
}
}
7. shouldParkAfterFailedAcquire方法
AQS源码第795行
// pred是上一个节点,node是当前节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 前驱节点的状态
int ws = pred.waitStatus;
// 如果上个节点状态为-1,则返回true
if (ws == Node.SIGNAL)
return true;
// 如果当前节点失效,则找一个不失效的节点
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
// 将重新标识好的上一个有效节点指向当前节点
pred.next = node;
} else {
// 小于等于0但是不为-1,基于CAS操作则将前驱节点的状态改为-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}