1、AQS
AQS本质上就是java中的AbstractQueuedSynchronizer
AQS的结构为双向队列
static final class Node {...}
private transient volatile Node head;
private transient volatile Node tail;
Node中的几个重要的属性:
static final class 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;
// 返回前驱节点,如果前驱节点为空,抛出异常
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
// 表示同步状态;如果可以从0变为1,说明拿到了锁资源,
// 如果为1, 2, 3说明其他线程占用了锁资源
private volatile int state;
}
2、加锁
- ReentranLock既可以实现公平锁,也可以实现非公平锁,也是互斥锁,以及可重入锁
public void lock() {
// sync分为公平锁和非公平锁
sync.lock();
}
- 以非公平锁为例
static final class NonfairSync extends Sync {
final void lock() {
// 通过CAS的方式尝试将state从0修改为1,如果返回为true,代表修改成功;
// 如果修改失败,返回false
if (compareAndSetState(0, 1))
// 将一个属性设置为当前线程,这个属性是AQS的父类提供的,表示当前前程拿到了锁资源
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
}
- acquire方法
public final void acquire(int arg) {
// tryAcquire再次尝试获取锁资源,如果尝试成功,返回true
if (!tryAcquire(arg) &&
// 获取锁资源失败后,将当前线程封装成一个Node追加到AQS的队列中
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 线程中断
selfInterrupt();
}
- tryAcquire方法:CTRL+T(!tryAcquire(arg) : 返回true的方法有两种,一个是拿到了锁资源,另一个是重入成功)如果返回了false,进行非操作后,就需要走acquireQueued方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取AQS的state的值
int c = getState();
// 如果为0,代表刚才使用锁资源的已经将锁资源释放,可以尝试竞争锁资源
if (c == 0) {
// CAS尝试修改state,从0到1,如果返回为true,代表修改成功;
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前占有锁资源的线程是否是当前线程,
else if (current == getExclusiveOwnerThread()) {
// 将c加一
int nextc = c + acquires;
// 如果加一之后小于0,说明超过最大锁数
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 重新对state赋值
setState(nextc);
// 锁重入成功
return true;
}
return false;
}
- addWaiter(Node.EXCLUSIVE)
// 说明获取锁资源失败
private Node addWaiter(Node mode) {
// 创建Node类,并且设置thread为当前线程,mode表示Node.EXCLUSIVE排他锁的标识
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 获取AQS中的尾部节点
Node pred = tail;
// 如果队列中有节点
if (pred != null) {
// node的前驱为尾部节点,所以node为tail节点
node.prev = pred;
// 使用CAS的方式,将当前tail节点转换为当前node节点
if (compareAndSetTail(pred, node)) {
// 之前尾结点的next为当前节点,并返回尾部节点
pred.next = node;
return node;
}
}
// 队列中无节点的方式,或前面CAS操作失败,也会进入:表示往队列尾部添加节点
enq(node);
return node;
}
private Node enq(final Node node) {
// 死循环,直到初始化成功
for (;;) {
// 获取tail节点
Node t = tail;
if (t == null) { // Must initialize
// 没有节点
if (compareAndSetHead(new Node())) // 初始化一个node作为head,没有意义
// 将头和尾都指向了这个节点(虚拟头结点) 第一次循环结束
tail = head;
} else {
node.prev = t;
// 有节点
// 使用CAS的方式,将当前tail节点转换为当前node节点
if (compareAndSetTail(t, node)) {
// 下一个节点next为node
t.next = node;
return t;
}
}
}
}
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 已将node加入到双向队列中,执行当前方法
final boolean acquireQueued(final Node node, int arg) {
// 获取锁资源的标识
boolean failed = true;
try {
// 标识
boolean interrupted = false;
for (;;) { // 死循环
// 获取当前节点的上一个节点,如果没有节点,就会抛出异常
final Node p = node.predecessor();
// 如果p(上一个节点)是头,就再次获取锁资源tryAcquire
if (p == head && tryAcquire(arg)) {
// 已经拿到锁资源了, 设置当前节点为头结点,将Thread和prev设置为null
setHead(node);
p.next = null; // help GC
failed = false; // 将标识修改为false,会影响到下面finally中的执行,如果为true
return interrupted; // 返回标识
}
// 保证上一个节点的状态是-1,才会返回true,会将线程阻塞,等待唤醒,获取锁资源。才会走下面的parkAndCheckInterrupt
if (shouldParkAfterFailedAcquire(p, node) &&
// 基于unsafe类的park方法挂起线程
parkAndCheckInterrupt()) // 这里是唯一可能出现的异常会抛出 jvm内部出现问题时,finally代码块的内容执行几率为0
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
setHead方法
// sethead里面
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
shouldParkAfterFailedAcquire方法
// 上文中调用时使用的是p, node表示的是前驱节点和当前节点
// 所以下文的pred是前驱节点,node是当前节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 前驱节点的状态
int ws = pred.waitStatus;
// 如果具有这个标识,说明后继节点需要被唤醒
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
// 如果上一个节点已经失效了,
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
// 将当前节点的前驱节点指向前驱节点的前驱
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0); // 一直找找到状态小于0的节点(-1)
// 前驱节点的前驱的后继为当前节点
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.
*/
// 小于等于0 但是不等于-1的情况,将上一个有效节点的状态修改为-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
compareAndSetWaitStatus方法
private static final boolean compareAndSetWaitStatus(Node node,
int expect,
int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,
expect, update);
}
parkAndCheckInterrupt方法
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
cancelAcquire方法
private void cancelAcquire(Node node) {
// 当前节点为null 结束 健壮性判断
// Ignore if node doesn't exist
if (node == null)
return;
// node的线程为空,竞争锁资源无关,线程也就没有用了
node.thread = null;
Node pred = node.prev;
// 如果前驱节点的状态大于0,说明为失效节点,知道找到一个有效节点
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// 将第一个不是失效节点的后继节点声明出来
Node predNext = pred.next;
// 当前节点置为失效节点
node.waitStatus = Node.CANCELLED;
// 如果当前节点为尾结点,将尾结点设置为前驱节点
if (node == tail && compareAndSetTail(node, pred)) {
// 用CAS的方式,将尾结点的next设置为null
compareAndSetNext(pred, predNext, null);
} else {
// 中间节点操作
int ws;
// 如果上一个节点不是头结点,
if (pred != head &&
// 上一个节点的状态,是不是有效
((ws = pred.waitStatus) == Node.SIGNAL ||
// pred需要唤醒后继节点,
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next); // 尝试将pred的next指向当前节点的next(有效的next的节点)
} else {
// 头部节点的操作
unparkSuccessor(node);
}
node.next = node; // help GC
}
}