基于AQS 分析 ReentrantLock


我这个人走得很慢,但我从来不后退。 ——林肯



代码案例

public class ReentrantLockDemo {
static int counter = 0;
static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
new Thread(() -> {
for(int i = 0; i < 10; i++) {
lock.lock();
System.out.println("线程名字"+Thread.currentThread().getName()+"加锁.........");
counter++;
System.out.println(counter);
lock.unlock();
System.out.println("线程名字"+Thread.currentThread().getName()+"释放锁........");
}
}).start();

new Thread(() -> {
for(int i = 0; i < 10; i++) {
lock.lock();
System.out.println("线程名字"+Thread.currentThread().getName()+"加锁");
counter++;
System.out.println(counter);
System.out.println("线程名字"+Thread.currentThread().getName()+"释放锁");
lock.unlock();
}
}).start();
}
}

代码运行结果

基于AQS 分析 ReentrantLock_加锁基于AQS 分析 ReentrantLock_sed_02

代码解释

开启了两个线程,通过加锁的方式每次保证只有一个线程对counter进行+1 保正了数据的安全性

源码分析

分析构造函数

关键代码

我们先创建了一个ReentrantLock的对象

ReentrantLock lock = new ReentrantLock();

看看构造函数里面都做了什么

public ReentrantLock() {
sync = new NonfairSync();
}

在构造函数里面new了一个NonfailSync实例,这个实例是干啥的呢?点进去看看底层的代码,看看这个NonfailSync是啥

// Sync object for non-fair locks
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}

NonfailSync 继承了 Sync,并且已经说明了这是一个非公平的锁,先留意一下这个lock()的方法,后面加锁有可能就是这个方法,看看这个Sync是干什么的?

public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
// ...
}
}

可以看出sync是ReentrantLock的一个属性,并且这个属性类实现了传说中的 AQS 类,先不往下看了,其实到这里就是在实例化ReentrantLock的时候就是创建了一个非公平的锁,这是ReentrantLock默认创建的。

分析加锁代码

lock.lock();

点进去看看

public void lock() {
sync.lock();
}

public ReentrantLock() {
sync = new NonfairSync();
}

static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
..... 省略
}

还记得不这个sync 就是构造函数中初始化的那个sync,这块调用的就是非公平锁里面的加锁的方法,我们来分析一下这个非公平锁加锁

非公平锁加锁

static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
..... 省略
}

先分析这个if模块

if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());

这里面有个 CAS 方法点进去看看

protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

底层用的是 unsafe 来操作的变量地址,这个变量地址是谁的地址呢,没错这个是state的变量的地址,期待值是0,如果是0的话将其改成1。那这个state是什么东西呢?往上找

public abstract class AbstractQueuedSynchronizer{
private static final Unsafe unsafe = Unsafe.getUnsafe();
private volatile int state;
private static final long stateOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
.... 省略
} catch (Exception ex) { throw new Error(ex); }
}
}

没错这个用volatile 修饰的state变量就是AQS的属性,该属性就是来看锁是否有人抢占加锁,如果已经不为0的话那么就证明有人加锁了,如果为0那么才有可能加锁成功。所以下面这个CAS操作成功才会走if里面

if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());

如果CAS设置成功的话那么将当前的独占线程设置成为自己,进去这个方法看看

public abstract class AbstractOwnableSynchronizer{
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
}

这个类 AbstractOwnableSynchronizer 是 AbstractQueuedSynchronizer 这个类继承的父类,通过该方法设置当前锁的独占线程,那么CAS 失败了怎么办呢?走下面的方法

acquire(1);

点进去看看

public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

该方法也是AQS类中的方法,先进入tryAcquire(arg) 这块代码分析一下看看里面是什么?

static final class NonfairSync extends Sync {
..... 省略
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}

这个方法调用的是非公平锁里面 tryAcquire 方法,传进来的 acquires 是 1。进入这个方法 nonfairTryAcquire 里面看看,这个方法也是AQS中实现的方法。

final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取 state 变量
int c = getState();
// 如果当前变量 state 为0的话,进入if 里面
if (c == 0) {
// 再次利用 CAS 设置一下,将state 设置为 1,如果成功的话将当前独占的线程设置当前线程,返回true
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果state 要不是0 的话证明已经有人加过锁了,那么看看这个当前加的独占线程是不是目前要加锁的线程,如果是的话那么将state 值加1返回
// true,证明了ReentrantLock 是一个可重入锁
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 加锁失败返回 false
return false;
}

// 设置新的state的值
protected final void setState(int newState) {
state = newState;
}

tryAcquire(arg) 返回的false的话我们走 acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 方法,先看addWaiter(Node.EXCLUSIVE) 方法

private Node addWaiter(Node mode) { //此时传进来的 Node.EXCLUSIVE 是NULL
// 创建了一个NODE 对象
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}

一步一步分析这个addWaiter,这里面有一个Node 类 看看这个Node 类是干嘛的?【这个Node也是AQS中的节点】

static final class Node {
// 共享模式节点
static final Node SHARED = new Node();
// 独占模式节点
static final Node EXCLUSIVE = null;

// 线程已经被取消了
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
// 后继线程需要被唤醒
static final int SIGNAL = -1;
// 指示线程正在等待条件
static final int CONDITION = -2;
// 下一个获取共享应该无条件传播
static final int PROPAGATE = -3;
// 上面的几种状态 CANCELLED、SIGNAL、CONDITION、PROPAGATE
volatile int waitStatus;
// 当前节点的前驱节点
volatile Node prev;
// 当前节点的后继节点
volatile Node next;
// 当前放入Node的线程
volatile Thread thread;
// 链接下一个等待条件的节点
Node nextWaiter;
// 注释已经说明 Used by addWaiter
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
// 注释已经说明 Used by Condition
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}

在这里我们创建了一个Node 节点

基于AQS 分析 ReentrantLock_公平锁_03

Node node = new Node(Thread.currentThread(), mode); // mode = NULL 此时
static final class Node {
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
}

继续分析定义了一个 pred 变量等于tail,此时tail 还是 null 值

Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;

因为此时 pred 等于 null 所以走enq(node) 方法

private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}

方法中定义了一个for的死循环,定义了一个 变量 t = tail 因为tail 是null ,所以 t 也是null ,所以进入了if( t==null),其实我理解就是初始化一个队列的头节点,设置成功后tail指针和head指针都指向了空的node节点

// 队列的头节点
private transient volatile Node head;
private static final long headOffset;
static {
try {
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
} catch (Exception ex) { throw new Error(ex); }
}
compareAndSetHead(new Node()) // 创建了一个队列的空Node节点,设置成功后tail指针和head指针都指向了空的node节点

private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}

 基于AQS 分析 ReentrantLock_源码_04

因为死循环并没有进行中断和退出,所以进行下一次循环,此时 t 也指向了tail 也就是指向了空的node节点

基于AQS 分析 ReentrantLock_源码_05

但是此时t指针并不是空值了所以走到了else这里,这里的node是我们传入进来的 node,他的前驱节点指向了 t 指针指向的节点

node.prev = t;

基于AQS 分析 ReentrantLock_加锁_06

if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}

进入这里看看 compareAndSetTail(t, node)

private transient volatile Node tail;
private static final long tailOffset;
static {
try {
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
} catch (Exception ex) { throw new Error(ex); }
}
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}

将 tail 指针指向为新入队的节点,如果设置成功了将t指针指向的节点的后继节点指向 node

基于AQS 分析 ReentrantLock_sed_07

addWaiter(Node.EXCLUSIVE)方法走完了,该走这个方法了 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)进入该方法看看

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)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}

在这个方面里面又是一个for死循环,定义了一个 p 指针指向了刚才已经入队的节点的前驱节点基于AQS 分析 ReentrantLock_sed_08

此时p指针指向的空的那个head节点,所以p==head 成立,所以再次尝试获取锁 tryAcquire(arg),因为有可能这个时候之前占有该锁的线程已经将锁给释放掉了,再次走入到之前分析的方法里面

protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}

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) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

如果成功加锁走下面的方法

setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}

此时head指针指向了node节点,node节点的线程变为了null,node的前驱节点指向了null所以由这张图

基于AQS 分析 ReentrantLock_加锁_09  ----变成---> 基于AQS 分析 ReentrantLock_sed_10这张图的样子

p.next = null; // help GC
failed = false;

基于AQS 分析 ReentrantLock_sed_11

如果加锁又一次失败了,那么图也就是队列还是没变化的

基于AQS 分析 ReentrantLock_sed_12

if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;

进入该方法看看 shouldParkAfterFailedAcquire(p, node)

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { // 传进来的就是 p 节点
int ws = pred.waitStatus; // 此时 p 节点的 waitStatus 是 null
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);
}
return false;
}

此时 p 节点的 waitStatus 是 null ,所以走到这里面

private static final long waitStatusOffset;

static {
try {
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
} catch (Exception ex) { throw new Error(ex); }
}

compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
private static final boolean compareAndSetWaitStatus(Node node,int expect,int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,expect, update);
}

将空的头节点的waitStatus 设置成SIGNAL,只有前驱节点是SIGNAL 状态的情况下才能将当前线程挂起

基于AQS 分析 ReentrantLock_源码_13

进入下一次循环之后 又没有获取锁获取成功,那么 shouldParkAfterFailedAcquire(p, node) 这个方法里面

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) // 经过上一轮的循环 p 节点的 waitStatus 已经被设置成 Node.SIGNAL 所以返回true
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}

那么就会进入另一个方法 parkAndCheckInterrupt() 方法里面

private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}

通过LockSupport.park(this);将当前线程挂起

分析释放锁代码

public void unlock() {
sync.release(1);
}

进入下面的方法里面

public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}

进入tryRelease(arg) 方法,而且释放锁是部分公平锁和非公平锁的

protected final boolean tryRelease(int releases) {
int c = getState() - releases; // 计算当前还剩几把锁
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { // 如果当前计算完state等于那么将当前的独占线程设置为空
free = true;
setExclusiveOwnerThread(null);
}
setState(c); // 设置state 关键字
return free;
}
protected final void setState(int newState) {
state = newState;
}

如果释放锁成功,定义一个h指针,指向了队列的头节点

基于AQS 分析 ReentrantLock_sed_14

此时头节点不为空并且头节点的waitStatus 等于-1也不是0 所以走到了 unparkSuccessor(h); 方法

private void unparkSuccessor(Node node) {
int ws = node.waitStatus; // -1
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 设置头节点的等待状态为 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.park(this) 挂起的线程
LockSupport.unpark(s.thread);
}

基于AQS 分析 ReentrantLock_sed_15

回顾一下在哪里挂起的线程呢?

基于AQS 分析 ReentrantLock_sed_16

如果被唤醒的话那么会继续获取锁,现在的图是这个样子的 

 基于AQS 分析 ReentrantLock_公平锁_17

之后有定义了一个p指针指向了当前节点的前驱节点,咱们当前的前驱节点就是head节点,如果获取锁成功了那么走setHead(node)方法

private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}

基于AQS 分析 ReentrantLock_sed_18

到这里的时候等待获取锁的队列里面没有其他节点了 只有一个空的头节点了,到此时释放锁也分析完成了

基于AQS 分析 ReentrantLock_公平锁_19