ReentrantLock
定义
AbstractQueuedSynchronizer抽象队列同步器
为什么要引入?
在业务中,肯定要不少会用到并发,我们就要考虑用到锁或者同步器来实现我们要实现的目的。或者就是实现线程安全。
那么,如果我们要手写锁或者同步器的话,肯定五花八门,没有章法。而且实现的细枝末节也要思考的面面俱到。就会想有没有线程的东西让我们使用来简化锁或同步器的实现呢?
AbstractQueuedSynchronizer就是干这个用的。
什么是AQS?
-
AbstractQueuedSynchronizer是用来实现锁或者其他同步器组件的公共基础部分的抽象实现
-
是重量级基础框架及整个JUC体系的基石,主要用于解决锁分配给”谁“的问题。
-
整体就是一个抽象的FIFO队列来完成资源获取线程的排队工作,并通过一个int类变量(state)表示持有锁的状态
AQS的核心思想
如果线程请求的共享资源没有被其他线程占用(也就是共享资源空闲),那么就会将当前线程设置为共享资源的拥有这,并有state变量来记录共享资源已经被占用了。
如果线程请求的共享资源已经被其他线程占用,那么就需要一套线程阻塞以及线程唤醒时锁的分配机制。
AQS中用CLH锁实现了这个机制。
CLH 锁是对自旋锁的一种改进,是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系),暂时获取不到锁的线程将被加入到该队列中。AQS 将每条请求共享资源的线程封装成一个 CLH 队列锁的一个结点(Node)来实现锁的分配。在 CLH 队列锁中,一个节点表示一个线程,它保存着线程的引用(thread)、 当前节点在队列中的状态(waitStatus)、前驱节点(prev)、后继节点(next)。
AQS是JUC内容中最重要的基石
JUC下的重要的类,都或多或少都和AQS有关系。

所以理解AQS是我们理解JUC的第一步
锁和抽象队列同步器的关系
- 锁:面向的是使用锁的人,定义了程序员和锁交互的使用层API,隐藏了实现细节,你调用即可
- 抽象队列同步器:面向的是索的设计者,ava并发大神DoungLee,提出了统一规范并简化了锁的实现,将其抽象出来,屏蔽了同步状态管理、同步队列的管理和维护、阻塞线程排队和通知、唤醒机制等,是一切锁和同步组件实现的
有了抽象对列同步器,可以设计使用自己的锁。但是只有锁的话,就只能调用别人写好的锁的API
从RenentrantLock角度理解AQS
分析了在一个线程已经拿到了共享资源的情况下,第二个线程会怎么执行
RenentrantLock构造方法,默认是非公平的,但是可以通过传fair参数选择公平或者非公平
public ReentrantLock() {
sync = new NonfairSync();
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
既然是锁,那么就要从锁方法谈起了。
public void lock() {
sync.lock();
}
final void lock() {
// 尝试CAS将状态值state设为1
if (compareAndSetState(0, 1))
// 成功设置为1,就将共享资源的拥有者设置为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 失败就执行acquire方法
acquire(1);
}
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
成功的话,就将state状态值设置为1,失败的话,就执行acquire方法。
就算失败,还是会再次尝试获取共享资源tryAcquire(arg)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire(arg)方法
// 这个在ReentrantLock中自定义的同步器定义,继承了AQS
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// 这个方法在AQS中定义,final,子类不能重写
final boolean nonfairTryAcquire(int acquires) {
// 拿到当前线程
final Thread current = Thread.currentThread();
// 获取当前的线程值
int c = getState();
// 如果是0,表示之前的线程将共享资源释放了
if (c == 0) {
// CAS尝试将state的值从0设为1,因为同一时刻可能有多个线程都要抢占共享资源
if (compareAndSetState(0, acquires)) {
// 抢占成功,就设置自己为共享资源的拥有线程
setExclusiveOwnerThread(current);
// 返回true,也就是返回到tryAcquire(arg)这个方法,就不向下执行,就获取共享资源成功了
// public final void acquire(int arg) {
// if (!tryAcquire(arg) &&
// acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// selfInterrupt();
// }
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;
}
如果tryAcquire(arg)返回false,就会执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg))方法
先看addWaiter(Node.EXCLUSIVE)),会将当前线程封装为node放入队列中
先来看看Node.EXCLUSIVE是什么意思
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
// 一个标记,隐含着一个节点是以独占的方式等待
// 该节点是空
static final Node EXCLUSIVE = null;
...
}
转过头来看addWaiter(Node.EXCLUSIVE))相当于addWaiter(null))
private Node addWaiter(Node mode) {
// 创建一个CLH队列中的节点,将当前线程放进去
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 初始时,队列是空的,那么tail和head都是空的,就会执行enq(node)
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}

再来看enq(node)方法
图

/**
* Inserts node into queue, initializing if necessary. See picture above.
* 插入node节点到队列中,如果队列没有初始化话,会初始化。
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
// 死循环
for (;;) {
// 将CLH尾结点赋值给,t
// 第一次肯定是空,一定要初始化
Node t = tail;
if (t == null) { // Must initialize
// 就会执行compareAndSetHead(new Node()),是个new Node()不是传进来的node
if (compareAndSetHead(new Node()))
// tail也指向了head,就初始化成功了。
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
看compareAndSetHead(new Node())方法,CAS的方式设置头节点
private final boolean compareAndSetHead(Node update) {
// CAS方式将当前的AQS中的head节点设置为update,new Node()
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
初始化之后,因为是死循环,所以,继续执行
private Node enq(final Node node) {
for (;;) {
// 已经初始化成功,并且,tail指向了,初始化的时候创建的new Node()节点。哨兵或者虚拟节点。
Node t = tail;
if (t == null) {
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 执行到这,当前node的prev指向头节点
node.prev = t;
// CAS将tail的值从t换成node,也就是tail现在指向了node
if (compareAndSetTail(t, node)) {
// t的后驱节点就是node,现在的t就是哨兵节点。
t.next = node;
// 返回t,也就是哨兵节点
return t;
}
}
}
}
但是没有接到返回值,返回的是node
enq(node);
return node;
返回到了acquireQueued(node, arg))node就是装着,封装着线程的node
// arg = 1
final boolean acquireQueued(final Node node, int arg) {
// 定义一个是否添加队列失败状态变量
boolean failed = true;
try {
// 是否被打断状态变量
boolean interrupted = false;
// 死循环
for (;;) {
// 找node的前驱去节点,就是哨兵节点,也就是head
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);
}
}
tryAcquire(1),分析,第二次尝试了,假设获取锁失败,就会返回false
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;
}
就会执行下边的代码
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
// shouldParkAfterFailedAcquire(p, node)
// 是否应该阻塞获取失败的线程
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// ws默认值是0
int ws = pred.waitStatus;
// 0 != -1
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);
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.
*/
// 会执行到这,CAS将前驱节点的WaitStatus值设置为-1,表示有义务唤醒它的后驱节点,也就是放进链表里的阻塞的线程,如果设置成功的话,返回true
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
然后就会执行parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
// 调用park阻塞该线程
LockSupport.park(this);
// interrupted()是静态方法:内部实现是调用的当前线程的isInterrupted(),并且会重置当前线程的中断状态。返回false,并重置中断状态为false
return Thread.interrupted();
}
然后当前线程就阻塞住了。直到被唤醒,这个时候,因为Thread.interrupted();返回的是false,就不会执行interrupted = true;这个语句。
然后再执行死循环,抢状态值,如果还是没有成功,就还会被阻塞,如果成功了,就会执行下边代码
if (p == head && tryAcquire(arg)) {
setHead(node);
// 把p这个对象置空,等待回收
p.next = null; // help GC
// failed为false
failed = false;
// 正常唤醒的情况下返回false,如果唤醒的时候被打断了,就会返回true
return interrupted;
}
private void setHead(Node node) {
// 将node值赋给head
head = node;
// 将node封装的thead对象设置为null,因为已经获得共享资源了,就不用在队列里待着了
node.thread = null;
// 设置node的前驱节点为null
node.prev = null;
}
如果拿到共享资源,正常执行。
如果中间被意外的打断就会返回true
然后就会执行以下代码
selfInterrupt();
// 中断线程
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
总结,如果已经有线程拿到了共享资源
第二个线程会执行
acquire(1);方法然后再执行acquire(1)的时候先尝试再次获取共享资源,执行
tryAcquire(1)如果没有成功获取到,就会返回false。然后在执行
acquireQueued(addWaiter(null), arg))执行addWaiter(null),在这个方法中
enq(node);会初始化队列,也会将线程放到队列中。之后就会把该节点返回。执行
acquireQueued(node, arg)),在这个方法中还会尝试获取共享资源,如果获取失败就会阻塞。直到被唤醒,获取到共享资源。不然会一直重试,阻塞,唤醒,……,重试,阻塞,唤醒,直到拿到共享资源。如果在被唤醒之后,返回的还是true,那么,就算他获取到了共享资源啊,最后还是会被
selfInterrupt();中断的。
如果现在又有线程进来了,那么怎么执行呢
又有线程就是,又有一个线程执行了lock()方法
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
假设第一个线程还是没有释放,执行acquire(1)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire(1),最终会执行到以下代码
final boolean nonfairTryAcquire(int acquires) {
// 拿到当前线程
final Thread current = Thread.currentThread();
// 拿到state值,现在为1
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;
}
// 返回false
return false;
}
返回false之后,就会执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法
看addWater(Node.EXCLUSIVE),会将当前线程封装成Node放入队列中。
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
private Node addWaiter(Node mode) {
// 构造方法分别赋值给,thread和nextWaiter,这个会判断是不是共享模式
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 将tail的值赋值给pred
Node pred = tail;
// 现在肯定不是null
if (pred != null) {
// 下边就是将node节点放到双向链表中
// 将当前节点的前驱节点指向尾部
node.prev = pred;
// CAS将tail指向新加进来的节点
if (compareAndSetTail(pred, node)) {
// 将原先是tail的节点的next指向新的tail
pred.next = node;
// 返回这个节点
return node;
}
}
enq(node);
return node;
}
加入队列之后,就该执行acquireQueued(node, arg)
final boolean acquireQueued(final Node node, int arg) {
// 设置失败的状态量
boolean failed = true;
try {
// 设置是否中断的状态量
boolean interrupted = false;
for (;;) { // 相当于while(true)
// 拿到前驱节点
final Node p = node.predecessor();
// 在我们的假设情况下,p不等于head
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 第一个条件返回true
// 然后执行parkAndCheckInterrupt(),这个上边分析过,就会阻塞当前线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
总结,如果第三个线程进来了,首先尝试设置state值为1,肯定不成功
然后就执行acquire()
然后就是执行tryAcquire(),返回false
执行addWaiter(),将这个线程节点放到了队列的尾部
让然后执行acquireQueued,会执行parkAndCheckInterrupt()方法,阻塞该线程。
重入分析
假设第一个线程又执行了一次lock.lock()方法
前边的就不分析了,最终会执行到下边的方法
final boolean nonfairTryAcquire(int acquires) {
// 拿到当前的线程
final Thread current = Thread.currentThread();
// c = 1
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 相同
else if (current == getExclusiveOwnerThread()) {
// 将现在的state值+1赋值给nextc,也就是记录了重入次数
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 重写设置state的值
setState(nextc);
// 返回true,最终到!truAcquire()就是false。代码就执行完了
return true;
}
return false;
}
unlock方法分析
第一次unlock()
假设第一个线程要执行了
现在队列中已经有三个线程了,分析释放锁的源码
public void unlock() {
sync.release(1);
}
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(1)方法
protected final boolean tryRelease(int releases) {
// 获得当前的state值是2, 2 - 1 = 1 = c
int c = getState() - releases;
// 判断下当前线程是不是占用共享资源的线程,如果不是,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
// 设置free状态变量
boolean free = false;
// 现在的c=1,不执行if代码块
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 将state的值为c=1
setState(c);
// 返回false
return free;
}
tryRelease方法返回了false,到release方法中,也会返回false,执行完毕了。
第二次unlock()
还是会执行到ReentrantLock重写的tryRelease(1)方法中
protected final boolean tryRelease(int releases) {
// c = 1 - 1 = 0
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 成立
if (c == 0) {
// 将free的值设置为true
free = true;
// 将当前的占用共享资源线程变量设置为null
setExclusiveOwnerThread(null);
}
// 将state的值设置为0
setState(c);
// 返回true
return free;
}
到这个方法中,tryRelease(1)是true
public final boolean release(int arg) {
if (tryRelease(arg)) {
// 将head的值赋值给h
Node h = head;
// h不等于null,且h的waitStatus为-1
if (h != null && h.waitStatus != 0)
// 执行唤醒方法
unparkSuccessor(h);
return true;
}
return false;
}
unparkSuccessor(h)
// 传进去的是head节点
private void unparkSuccessor(Node node) {
// ws的值是-1,是被它的后驱节点设置上的。在shouldParkAfterFailedAcquire方法中的else分支中
int ws = node.waitStatus;
if (ws < 0)
// CAS将head节点的status值设置为0
compareAndSetWaitStatus(node, ws, 0);
// 将head节点的后驱节点赋值给s,这时候s的值就是第二个线程的值了
Node s = node.next;
// s不是null,且s的waitStatus是-1,不成立
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)
// 调用unpark()唤醒线程
LockSupport.unpark(s.thread);
}
唤醒之后,就会执行到parkAndCheckInterrupt()。从阻塞状态苏醒过来。parkAndCheckInterrupt() = false
然后重新执行for(;😉,因为现在state是0了,是能抢占的。抢占成功,就更新链表。返回false
final boolean acquireQueued(final Node node, int arg) {
// 定义一个是否添加队列失败状态变量
boolean failed = true;
try {
// 是否被打断状态变量
boolean interrupted = false;
// 死循环
for (;;) {
// 找node的前驱去节点,就是哨兵节点,也就是head
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);
}
}
能看到非公平锁和公平锁的区别,非公平锁是存在后来的线程抢占的。
现在这种情况,第一个线程释放了,按理说,队列中的第一个节点理应获得共享资源,但是再回过头来看,lock()方法,还是会跟刚刚唤醒的线程竞争。如果竞争不过,就又阻塞了
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
再来看公平锁的lock()方法,就是公平的方法。
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 会判断下是否有前驱节点,如果有前驱节点就返回false,
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 返回false之后,就会执行addWaiter(),将新来的线程加入到队列中。
return false;
}
还有最后一点没有分析。等有时间再分析
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;
}

浙公网安备 33010602011771号