AQS概述
一、 AQS本质就是AbstractQueuedSynchronizer类;
1、AQS一些说明
在AQS中有一些属性和一个双向对列(CLH对列);
AQS是并发包下的一个基类,AQL中关键的内部类;
2、node对象
static final class Node {
//独占锁/排它锁 标识
static final Node EXCLUSIVE = null;
//如果有这个标识, 证明已经失效了
static final int CANCELLED = 1;
//如果有这个标识, 说明后续节点需要被唤醒
static final int SIGNAL = -1;
//node 对象储存标识的地方
volatile int waitStatus;
//上一个节点
volatile Node prev;
//下一个节点
volatile Node next;
//当前node 绑定的线程
volatile Thread thread;
//返回前驱节点/上一个节点,
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
}
3、AbstractQueuedSynchronizer中包含
记录头节点
private transient volatile Node head;
记录尾部节点
private transient volatile Node tail;
Node
ReentrantLock可以实现公平锁也可以实现非公平锁,同时也是互斥锁以及可重入锁
二、ReentrantLock加锁
1、进入lock
public void lock() {
//sync 分为公平与非公平锁
//有俩种实现
sync.lock();
}
2、从非公平锁实现进入
final void lock() {
//通过CAS(比较交换)的方式尝试将state由0改为1,返回true表示成功,返回false,表示失败
if (compareAndSetState(0, 1))
//将一个属性设置为当前线程,这个属性是由AQS父类提供
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
3、 acquire方法
public final void acquire(int arg) {
//再次尝试获取锁资源,tryAcquire(arg) 返回true 表示已经拿完锁资源,
if (!tryAcquire(arg) &&
//!tryAcquire(arg) 获取资源失败, 需要将当前线程封装成node, 追加到AQS对列中
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//
selfInterrupt();
}
4、acquireQueued方法
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取AQL中state值
int c = getState();
//如果AQS中状态为0 表示上一个线程已经执行完毕,可以竞争锁
if (c == 0) {
//通过CAS尝试将AQS中状态由0改为1,返回成功,设置setExclusiveOwnerThread属性为当前线程
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果状态不为0,肯定有线程获取到了锁,判断是否为当前线程
else if (current == getExclusiveOwnerThread()) {
//如果是当前线程则,为AQS状态加 acquires
int nextc = c + acquires;
//如果重入加锁太多超过最大值,抛出加锁最大异常
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//成功累加,重新赋值state
setState(nextc);
//锁重入成功
return true;
}
return false;
}
5、addWaiter(Node.EXCLUSIVE)方法
//前边获取锁资源失败,放到对列中等待
private Node addWaiter(Node mode) {
//创建node 设置当前线程为新node对象, 同时设置为 排它锁 mode =Node.EXCLUSIVE
Node node = new Node(Thread.currentThread(), mode);
//获取AQS最后一个节点node
Node pred = tail;
if (pred != null) {
//最后一个不为空,说明对列中有等待节点,将当前节点的前一个节点设置为,最开始的最后一个节点
node.prev = pred;
//通过CAS的方式尝试 将AQS的tail最后一个节点设置为当前节点
if (compareAndSetTail(pred, node)) {
//设置成功,将当前节点设置为最后一个节点
pred.next = node;
return node;
}
}
enq(node);
return node;
}
//enq方法 尾部节点为空时执行,
private Node enq(final Node node) {
for (;;) {
//继续尝试获取尾节点
Node t = tail;
//如果继续为空
if (t == null) { // Must initialize
//使用CAS 设置一个无意义的头节点
if (compareAndSetHead(new Node()))
//同时尾节点也指向当前节点
tail = head;
} else {
//存在尾结点,将当前节点的上一个节点设置为t,
node.prev = t;
//通过CAS的方式尝试 将AQS的tail最后一个节点设置为当前节点
if (compareAndSetTail(t, node)) {
//设置成功,将当前节点设置为最后一个节点
t.next = node;
return t;
}
}
}
}
6、acquireQueued方法
// 已经将node加入到双向对列中后,执行此方法
final boolean acquireQueued(final Node node, int arg) {
//设置一个标识
boolean failed = true;
try {
//设置一个标识
boolean interrupted = false;
for (;;) {
//获取当前节点的前驱节点/前一个节点
final Node p = node.predecessor();
//如果P节点是头节点,当前节点尝试再次获取锁资源(state 0->1,或者可重入),成功true
//失败false
if (p == head && tryAcquire(arg)) {
//获取锁资源成功,将当前节点设置为头节点
setHead(node);
//将之前的头节点置空,利于GC
p.next = null;
//修改标识,
failed = false;
//返回标识
return interrupted;
}
//没拿到锁资源,
if (shouldParkAfterFailedAcquire(p, node) &&
//shouldParkAfterFailedAcquire方法返回true时,阻塞当前线程等待唤醒
//基于UNSAFE.park方法挂起线程
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//pred上一个节点。node当前节点 当前方法作用保证上一个节点为-1时 才返回true
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//获取上一个节点的状态
int ws = pred.waitStatus;
//上一个节点状态为SIGNAL=1 时 正常。
if (ws == Node.SIGNAL)
return true;
//如果上一个节点失效了,
if (ws > 0) {
do {
// 将当前节点的上一个节点,指向到上一个节点的前一个节点
//C -->B-->A 原指向规则
//C-->A 修改后指向规则
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
//将重新声明好的上一个节点指向为当前节点
// A -->B -->C 原指向规则
//A-->C 修改后指向规则
pred.next = node;
} else {
//当ws 小于等于0,并且不等于-1 时 ,将当前节点的上一个节点设置为-1 ,只有上一个节点
//状态为-1 时,才可以正常唤醒下一个节点
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}