AbstractQueuedSynchronizer是实现同步的基础组件,简称AQS,JUC的锁底层就是AQS实现的。ReentrantLock是AQS的一个实现。先看类图结构:
ReentrantLock
ReentrantLock实现了Lock接口
1.成员变量
很简单,一个Sync类的snyc
2.方法
3.内部类
Sync(成员变量不讨论)
Sync继承了AbstractQueuedSynchronizer抽象类
1.方法:
NonfairSync(成员变量不讨论)
1.方法:
FairSync(成员变量不讨论)
1.方法:
AbstractQueuedSynchronizer
1.成员变量
2.内部类
Node
1.成员变量
ConditionObject:以后讨论
正片开始
一、看源码前知识
part.1 AbstractQueuedSynchronizer
AQS组件介绍:
-
AQS是一个FIFO的双向队列,其中head和tail节点来维护链表队列。它们初始状态是head = tail = null。
-
队列的元素是AQS的内部类Node。
- Node有next和prev指针来维护双向队列。
- Node有一个waitStatus来记录本节点的阻塞状态
-
AQS有一个非常重要的成员变量state。它是实现锁的核心,在不同实现的锁中代表不同的意义。但是锁都通过原子性地改变state的值,实现同步。state相对于一个标记。
- 对于ReentrantLock:state是获得锁线程可重入次数
- 对于ReentrantReadWriteLock:state高16位表示读锁获取次数,低16位表示写锁的可重入次数。
- 对于Semaphore:state表示信号量个数
- 对于CountDownLatch:state表示计数器当前值
-
acquire()方法:调用实现类的tryAcquire()方法来试图获取锁,tryAcquire()有两个结果:
- 获取锁(或重入锁)成功,设置当前线程为持有锁线程,返回true。
- 获取锁失败,返回false。
- 如果tryAcquire()返回false,那么acquire()会调用addWaiter()方法和acquireQueued()方法来把未获得锁的该线程装进一个new出来的Node中,并把该Node放入AQS阻塞队列,并且acquireQueued()会调用parkAndCheckInterrupt()方法通过 LockSpport类来park()该线程,让该线程阻塞。(该过程还存在2次自旋、AQS阻塞队列是否初始化的问题)
-
tryAcquire()方法:上面已经叙述。⚠️tryAcquire()由AQS的子类实现。作用是通过改变state的值获取锁。并且返回获取锁成功还是失败的boolean值。返回给acquire()方法。
-
addWaiter()方法:为阻塞线程new出Node并且入队。
-
acquireQueued()方法:在AQS队列已存在的情况下,该方法直接把新的Node装载的线程park()掉。对创世线程,该方法会两次自旋再次检查锁能否被该线程获取,不能的话最后也会将装载该线程的Node放入刚刚初始化的AQS阻塞队列(该过程会初始化AQS阻塞队列)。
-
release()方法:
-
tryRelease()方法:
-
parkAndCheckInterrupt()方法
AQS思想介绍:
- AQS核心就是通过LockSpport的park方法来阻塞队列。
- 线程通过少量的自旋来检查并尝试获取锁。
- AQS通过head、tail以及Node类来维护链表队列。
- 线程间同步(加锁)通过原子性地修改state实现。
AQS结合ReentrantLock的流程介绍:
- 当多个线程调用ReentrantLock的lock.lock()时,只有一个线程t1获取到了锁。第二个线程t2会初始化AQS队列并且转换为Node节点入队阻塞队列(线程t2还会进行两次自旋操作来检查锁是否在初始化AQS队列的过程中被释放来,被释放了的化就立刻获取锁),其他线程也会转换为Node节点入队阻塞队列。
- 如果获取到锁的t1线程在执行临界区时,发现自己不满足条件,于是调用条件变量conditionObject的await()方法。那么t1会释放锁,并且被转换为Node节点进入该条件变量维护的条件队列中。
- 这时候,AQS队列中的线程也会被唤醒并且自旋尝试获取锁。如果获取了锁但是也调用了条件变量conditionObject的await()方法,那么也将被转换为Node节点进入该条件变量维护的条件队列中。
- 当另外一个线程调用了conditionObject的signalAll()方法时,会把条件队列里的一个或全部Node移动到AQS队列中,等待获取锁。
二、看源码(以公平锁为例)
第一个线程
- Test.java (测试类)
假设第一个线程执行了如下代码:
ReentrantLock lock2 = new ReentrantLock(true);
lock.lock(); < — — — — — a.进去
- ReentrantLock
public void lock() {
sync.lock(); < — — — — — b.进去
- ReentrantLock.Sync
abstract void lock(); < — — — — — c.进实现类去
- ReentrantLock.FairSync
final void lock() {
acquire(1); < — — — — — d.进夫父类去
}
- AbstractQueuedSynchronizer
先执行tryAcquire(arg),其中arg = 1
// 1.该方法先执行tryAcquire(arg),返回true就走了。返沪false则执行&&右边
// tryAcquire(arg)是为了加锁(设state)
// 2.执行addWaiter(Node.EXCLUSIVE):生成node(初始化队列)并入队
// 3.执行acquireQueued()去再次自旋获取锁,失败就park阻塞线程
public final void acquire(int arg) {
if (!tryAcquire(arg) && < — — — — — e.进夫实现类去
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- ReentrantLock.FairSync
// 该方法主要通过CAS设置state来获得锁
// 该方法是公平锁策略,拿锁前要判断是否排队
// 或者增加重入数量
protected final boolean tryAcquire(int acquires) {
// 获得该线程实例
final Thread current = Thread.currentThread();
// 获得此时的state值,默认 state = 0
int c = getState();
// c == 0,进入下面if语句
if (c == 0) {
// 判断是否需要排队,返回false,执行第二句compareAndSetState(0, acquires))
// cas成功,进入if
// 执行setExclusiveOwnerThread(current):把当前线程设为持有锁线程
if (!hasQueuedPredecessors() &&
// 设置state = 1
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true; < — — — — — f.返回e:true
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
- 最终返回
ReentrantLock lock2 = new ReentrantLock(true);
lock.lock(); < — — — — — g.返回结束,获取了锁
第二个线程
- Test.java (测试类)
现在第一个线程已经获得了锁,且持有锁没放。第二个线程现在过来试图获取锁
ReentrantLock lock2 = new ReentrantLock(true);
lock.lock(); < — — — — — a.进去
- ReentrantLock
public void lock() {
sync.lock(); < — — — — — b.进去
- ReentrantLock.Sync
abstract void lock(); < — — — — — c.进实现类去
- ReentrantLock.FairSync
final void lock() {
acquire(1); < — — — — — d.进夫父类去
}
- AbstractQueuedSynchronizer
先执行tryAcquire(arg),其中arg = 1
// 1.该方法先执行tryAcquire(arg),返回true就走了。返沪false则执行&&右边
// tryAcquire(arg)是为了加锁(设state)
// 2.执行addWaiter(Node.EXCLUSIVE):生成node(初始化队列)并入队
// 3.执行acquireQueued()去再次自旋获取锁,失败就park阻塞线程
public final void acquire(int arg) {
if (!tryAcquire(arg) && < — — — — — e.进夫实现类去
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- ReentrantLock.FairSync
// 该方法主要通过CAS设置state来获得锁
// 该方法是公平锁策略,拿锁前要判断是否排队
// 或者增加重入数量
protected final boolean tryAcquire(int acquires) {
// 获得该线程实例
final Thread current = Thread.currentThread();
// 获得此时的state值,由于有线程1持有锁,所以state = 1
int c = getState();
// c == 1,跳过下面if语句
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 由于该线程不是持有锁线程,所以跳过else if语句
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false; < — — — — — f.最终返回false返回e
}
- AbstractQueuedSynchronizer
public final void acquire(int arg) {
// tryAcquire(arg)返回false,!tryAcquire(arg) 为true,执行 && 右
if (!tryAcquire(arg) &&
// 调用(addWaiter(Node.EXCLUSIVE)方法
// 调用acquireQueued()方法
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) < — — — — — g.进入(addWaiter(Node.EXCLUSIVE)方法
selfInterrupt();
}
- AbstractQueuedSynchronizer
// 该方法生成一个新的Node,装载该线程
// 调用enq(node)方法入队/初始化阻塞队列
private Node addWaiter(Node mode) {
// 新建一个Node,装载当前进程
Node node = new Node(Thread.currentThread(), mode);
// Node的pred指向tail,此时tail = head = null
Node pred = tail;
// 跳过if语句
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node); < — — — — — h.进入enq(node)方法
return node;
}
- AbstractQueuedSynchronizer
//该函数初始化了队列,新建了head的Node节点。并且将node入队
private Node enq(final Node node) {
// 无限循环
for (;;) {
// [循环1:t就是tail]
// [循环2:t就是tail]
Node t = tail;
// [循环1:现在tail == t == null,进入if]
// [循环2:现在t!= null, 跳过if]
if (t == null) {
// [循环1:CAS设置头部head为一个新建的空的Node]
if (compareAndSetHead(new Node()))
// [循环1:tail == head == new Node(),如下图2]
tail = head; // [循环1:图3] 循环1结束
// [循环2:进入else]
} else {
// [循环2:node的prev指向t(tail)]
node.prev = t; // [循环2:图4]
// [循环2: CAS设置尾为node]
if (compareAndSetTail(t, node)) {
// [循环2: 前节点的next指向node]
t.next = node; //[循环2:图4]
return t; < — — — — i.返回h
}
图1
图2
图3
图4
- AbstractQueuedSynchronizer
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
// 由于acquireQueued()方法阻塞,此处也阻塞等待acquireQueued返回
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) < — — — — — j.进入acquireQueued()方法
selfInterrupt();
}
- AbstractQueuedSynchronizer
// 1.该函数是自旋的死循环,打破的地方在唯一的return处
// 2.该函数第2次循环,才把线程park掉。给了它两次循环的机会
// 3.park掉后线程就在park的地方阻塞了,等待unpark唤醒
// 4.第2点,如果是不是队列来的第一个node,那么它前继不是head,那么它不会走第一个if。它会等到它的prev是head的时候,才有机会返回。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
// 死循环
for (;;) {
// [循环1:p是node的前节点,也就是空空的head]
// [循环2:同上]
final Node p = node.predecessor();
// [循环1:p是head, 所以此时执行tryAcquire(arg)。其实就是在去尝试获得一次锁,相对于第一次自旋]
// [循环1: 因为有可能在初始化队列的过程中, 线程1已经释放锁了]
// [循环1: 能拿到锁就尽量拿,免得去park。park会调用操作系统资源,很消耗
// 如果tryAcquire(arg)成功,则进入if,则setHead(node):这里不展开。大概就是把线程2出队,node的thread内容情况,变成一个空的head
// [循环1:此处tryAcquire(arg)失败,跳过if]
// ----------------------------------------
// [循环2:同上, 第二次自旋尝试获得锁,失败,跳过if]
// ...很久以后的开始循环3
// [循环3次: 终于tryAcquire(arg)成功,可以获得锁了,进入if
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
// [循环3: 返回true
return interrupted; < — — — — — m.返回false
}
// [循环1: 执行shouldParkAfterFailedAcquire(p, node)
// [循环1: 该函数很复杂,大概就是让它不失败,再去自旋一次取锁
// [循环1: 它会设置head的waitStatus为-1,并且返回false
// [循环1: 那么跳过if。死循环第二轮开始]
// ---------------------------------------------
// [循环2: 执行shouldParkAfterFailedAcquire(p, node)]
// [循环2: 这次执行就返回true]
// [循环2: 执行parkAndCheckInterrupt()]
// [循环2: 会调用LockSupport.park(this)
// [循环2: 终于park了,线程在次停住。等待parkAndCheckInterrupt()返回
// ...很久以后
// [循环2: 很久以后,parkAndCheckInterrupt()终于返回了false,循环2继续,跳过if
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) < — — — — — k.进入parkAndCheckInterrupt()方法
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
- AbstractQueuedSynchronizer
private final boolean parkAndCheckInterrupt() {
// 1.park阻塞此线程,线程在停住。等待unpark唤醒
LockSupport.park(this);
// ...过了很久
// 2. 线程1释放lock,线程2在此处唤醒,被unpark()唤醒了
// 返回Thread.interrupted(),返回false
return Thread.interrupted(); < — — — — — l.返回false
}
- AbstractQueuedSynchronizer
public final void acquire(int arg) {
// 现在!tryAcquire(arg)为true
// acquireQueued(addWaiter(Node.EXCLUSIVE)终于返回false
// 所以整体为false,跳过if体
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
} < — — — — n.返回d、c、b、a 结束
- AbstractQueuedSynchronizer
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
以上13步,还有一中可能是被park的线程2被中断唤醒,那么parkAndCheckInterrupt()方法就会返回true。又是另外一番故事了
收工!
释放锁
第一个线程释放锁unlock源码
- Test.java (测试类)
现在第一个线程执行了如下代码,释放锁:
lock.unlock(); < — — — — — a.进去
- ReentrantLock
public void unlock() {
sync.release(1); < — — — — — b.进去
}
- AbstractQueuedSynchronizer
public final boolean release(int arg) {
// 此时arg = 1,执行tryRelease(arg)
if (tryRelease(arg)) { < — — — — — c.进去tryRelease(arg)
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
- ReentrantLock
protected final boolean tryRelease(int releases) {
// c = 1 - 0。由于线程1持有锁,并且重入数为1,所以getState()==1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 进入if
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 设置state为0
setState(c);
// 返回true
return free; < — — — — — d.返回false到c
}
- AbstractQueuedSynchronizer
public final boolean release(int arg) {
// tryRelease(arg)返回true,进入if
if (tryRelease(arg)) {
// h 为那个空空的头
Node h = head;
// h不为空,waitStatus == -1,进入if
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); < — — — — — e.进入unparkSuccessor(h)
return true;
}
return false;
}
- AbstractQueuedSynchronizer
private void unparkSuccessor(Node node) {
// 传入的h为空空的head,ws = -1
int ws = node.waitStatus;
// 进入if
if (ws < 0)
// CAS设置h的waitStatus为0
compareAndSetWaitStatus(node, ws, 0);
// 得到线程2的Node,用s指向它
Node s = node.next;
// 线程2的Node不为空,且线程2的Node的waitStatus == -1,跳过if
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;
}
// unpark(线程2)
if (s != null)
LockSupport.unpark(s.thread); < — — — — 被阻塞的线程2被唤醒,结束
入队的其他两种情况
假设此时阻塞队列已经初始化:
情况一、队列中元素>1个
假设t1已加锁,t2已进入阻塞队列,t3也已进入阻塞队列排在t2后。此时t1释放了锁,阻塞队列的空空的head被gc清理。t2的node清空thread成员,晋升为阻塞队列的head,此时阻塞队列有t2(head)——>t3。
那么此时,t2被唤醒,自旋调用tryAcquire()方法,还要检查自己是否需要排队。
ReentrantLock
protected final boolean tryAcquire(int acquires) {
// current == t2
final Thread current = Thread.currentThread();
// c == 0
int c = getState();
// 进入if
if (c == 0) {
// 检查是否需要排队hasQueuedPredecessors()
if (!hasQueuedPredecessors() && < — — — — — a.进入
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
AbstractQueuedSynchronizer
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
// (h != t)为true
//((s = h.next) == null) 为false
//(s.thread != Thread.currentThread())s是t3,所以为false
// 整体返回 false
return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); < — — — — — b.返回false
}
ReentrantLock
protected final boolean tryAcquire(int acquires) {
// current == t2
final Thread current = Thread.currentThread();
// c == 0
int c = getState();
// 进入if
if (c == 0) {
// hasQueuedPredecessors()返回false
// (!hasQueuedPredecessors())整体为true
// 执行 && 右边CAS,试图获得锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
情况二、队列中元素 = 1个
t1刚刚释放,head被清理。队列只有t2,t2的node晋升为空空的head。此时就只有一个元素。t2唤醒自旋、调用tryAcquire()方法。经过判断,hasQueuedPredecessors()返回 -true- false,因为此时head == tail。
AbstractQueuedSynchronizer
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
// (h != t)为false
return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); < — — — — — b.返回false
}
分析 lock.lockInterruptibly()
可以看到,在源码分析13步:
13. AbstractQueuedSynchronizer
private final boolean parkAndCheckInterrupt() {
// 1.park阻塞此线程,线程在停住。等待unpark唤醒
LockSupport.park(this);
// ...过了很久
// 2. 线程1释放lock,线程2在此处唤醒,被unpark()唤醒了
// 返回Thread.interrupted(),返回false
return Thread.interrupted(); < — — — — — l.返回false
}
在这里似乎Thread.interrupted()正常情况一定返回false,因为没有打断。就算打断了,返回false,一路返回回去,也没有任何效果,线程会继续尝试获取锁。所以看出这段代码在lock.lock()中没有任何意义。那到底什么意思呢?其实是因为这段代码被lock.lockInterruptibly()复用了,它在lock.lockInterruptibly()里才起了作用。可能是作者偷懒了。来看lock.lockInterruptibly():
- Test类
try {
lock.lockInterruptibly(); < — — — — — a.进入
} catch (InterruptedException e) {
System.out.println("lockInterruptibly throws");
- ReentrantLock
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1); < — — — — — b.进入
}
- AbstractQueuedSynchronizer
public final void acquireInterruptibly(int arg)
throws InterruptedException {
//先检查本线程有没有打断过,有的话就抛出异常
if (Thread.interrupted())
throw new InterruptedException();
// 尝试获取锁,失败就进入if(此时假设有t1持有,t2失败)
if (!tryAcquire(arg))
doAcquireInterruptibly(arg); < — — — — — c.进入
}
- AbstractQueuedSynchronizer
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
// 依旧,在自旋2次拿不到锁的时候,进入parkAndCheckInterrupt()去park阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) < — — — — — c.进入
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
- AbstractQueuedSynchronizer
private final boolean parkAndCheckInterrupt() {
// 再次阻塞,要么等待unpark唤醒,要么等死直到interrupt唤醒
LockSupport.park(this);
// 如果interrupt唤醒,返回true
return Thread.interrupted(); < — — — — — d.返回true
}
- AbstractQueuedSynchronizer
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException(); < — — — — — e.抛出异常,结束
}
} finally {
if (failed)
cancelAcquire(node);
注意⚠️
在源码分析14步,该过程是lock.lock()调用的
14. AbstractQueuedSynchronizer
public final void acquire(int arg) {
// 现在!tryAcquire(arg)为true
// acquireQueued(addWaiter(Node.EXCLUSIVE)终于返回false
// 所以整体为false,跳过if体
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
} < — — — — n.返回d、c、b、a 结束
如果lock.lock()阻塞过程中,有人中断了t2线程。也就是
t2.interrupt();
那么13步,会返回true,Thread.interrupted() == true
13. AbstractQueuedSynchronizer
private final boolean parkAndCheckInterrupt() {
// 1.park阻塞此线程,线程在停住。等待unpark唤醒
LockSupport.park(this);
// ...过了很久
// 2. 线程1释放lock,线程2在此处唤醒,被unpark()唤醒了
// 返回Thread.interrupted(),返回false
return Thread.interrupted(); < — — — — — l.返回false
}
那么14步会执行if语句体:selfInterrupt()
AbstractQueuedSynchronizer
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
这一步是什么意思呢?之前说它毫无意义,是作者偷懒。是的,但是这一步就是一种补救。补救在lock.lock()被中断的情况下,Thread.interrupted()返回了true,但是该方法会清零中断标志,所以下一次调用会返回false,等用户自己想来获取t2的中断标志时就会返回false。这样就不对,所以执行selfInterrupt()再次中断一次,把中断标志归位。这样以后用户想获取t2中断状态时,就能得到true。