文章目录
加锁流程概述
公平锁和非公平锁加锁流程不同处
公平锁
- 判断当前是否有线程持有锁
- 若有,判断是否是当前线程,若是当前线程则重入。重入次数超过上限(int 最大值)则抛异常。
- 若不是当前线程持有锁,则将当前线程封装成 Node 进入队列排队。
- 若没有线程持有锁,则判断队列中是否有其他线程正在排队。
- 若没有线程排队或者当前线程排在第一个,则基于CAS的方式将 state 修改为1。若修改成功则表示获取锁成功,将当前线程设置到 exclusiveOwnerThread,代表是当前线程持有锁资源。 设置中断标记位selfInterrupt()。
- 若有其他线程在排队或CAS修改失败,将当前线程封装成 Node 进入队列排队。
- 若有,判断是否是当前线程,若是当前线程则重入。重入次数超过上限(int 最大值)则抛异常。
非公平锁
- 基于CAS的方式,尝试将state从0改为1
- 若修改成功则表示获取锁成功,将当前线程设置到 exclusiveOwnerThread,代表是当前线程持有锁资源。
- 若修改失败则判断当前是否有线程持有锁
- 若没有线程持有锁,则基于CAS的方式,尝试将state从0改为1。若修改成功则表示获取锁成功,将当前线程设置到 exclusiveOwnerThread,代表是当前线程持有锁资源。设置中断标记位selfInterrupt()。
- 若有线程持有锁则判断是否是当前线程,若是当前线程则重入。重入次数超过上限(int 最大值)则抛异常。
- 若有其他线程在排队或CAS修改失败,将当前线程封装成 Node 进入队列排队。
公平锁和非公平锁加锁流程相同处
- 线程封装成 Node 进入队列排队
- 判断当前结点的前继结点是否是 head 结点,若是则尝试获取锁
- 若不是则将当前结点挂起等待唤醒
lock() 方法公平锁和非公平锁流程图
加锁流程源码解析
lock()
ReentrantLock 中的 lock() 方法分为公平锁和非公平锁两种实现。
public void lock() {
// sync 是 ReentrantLock 的一个内部类
// abstract static class Sync extends AbstractQueuedSynchronizer
sync.lock();
}
公平锁实现
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 查看队列中是否有正在排队的线程:1. 没线程排队 2. 有线程排队,但是第一个(不包括伪头)就是当前线程
if (!hasQueuedPredecessors() &&
// 基于 CAS 的方式,尝试将 state + 1(从 0 改成 1)
compareAndSetState(0, acquires)) {
// 设置当前持有锁的线程是当前线程
setExclusiveOwnerThread(current);
return true;
}
}
// 当前持有锁的线程就是当前线程,重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
// 判断是否溢出,可重入次数最大为 int 最大值。
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
hasQueuedPredecessors()
// 查看是否有除了当前线程以外的线程在队列中排队
// 返回 false 说明没有线程在排队,可以竞争锁
public final boolean hasQueuedPredecessors() {
// 尾结点
Node t = tail;
// 头结点
Node h = head;
Node s;
// 返回 false 的两种情况:
// 1 h == t 即头尾结点都指向伪头结点
// 2 当前排队的第一个结点(除了伪头结点)就是当前线程
// s = h.next;
// s != null && s.thread == Thread.currentThread()
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
非公平锁实现
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() {
// 先基于 CAS 的方式,尝试将 state 修改为 1
if (compareAndSetState(0, 1))
// 获取锁资源成功并将当前线程设置到 exclusiveOwnerThread,代表是当前线程持有锁资源
// private transient Thread exclusiveOwnerThread 属性在 AQS 的父类 AbstractOwnableSynchronizer 中
// 此处没有设置中断标志位
setExclusiveOwnerThread(Thread.currentThread());
else
// 尝试获取锁资源
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
// 非公平锁实现
// acquire(1) -> tryAcquire(1) -> nonfairTryAcquire(1) 故此处 acquires 的值为 1
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取 state 属性
int c = getState();
// 判断 state 是否为 0,说明持有锁的线程释放了锁资源
if (c == 0) {
// 再次通过 CAS 将 state 从 0 修改为 1
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
// 抢到锁了返回 true
return true;
}
}
// 判断持有锁的线程是否是当前线程
else if (current == getExclusiveOwnerThread()) {
// 是当前线程,证明是锁重入操作,将 state + 1
int nextc = c + acquires;
// int 溢出了,可重入次数的上限
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 将 nextc 赋值给 state
setState(nextc);
// 锁重入成功
return true;
}
// 获取锁失败
return false;
}
acquire()
ReentrantLock 的公平锁和非公平锁使用同一套 acquire() 逻辑。但是公平锁和非公平的tryAcquire(arg) 实现不同。
public final void acquire(int arg) {
// tryAcquire:查看当前线程是否可以尝试获取锁资源:1 其他线程刚好释放了锁 2 当前线程锁重入操作
if (!tryAcquire(arg) &&
// 当前线程没有获取到锁资源
// 先执行 addWaiter(Node.EXCLUSIVE) 将当前线程封装为 Node 结点,插入到 AQS 双向链表的末尾
// acquireQueued 查看当前结点是否是第一个排队的结点,若是则再次尝试获取锁资源,若长时间拿不到则挂起线程
// 若不是第一个排队的结点,则挂起线程
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 设置中断标记位,在 tryLock() 方法中会用到
selfInterrupt();
}
addWaiter()
// 没竞争到锁资源的线程封装成 Node 进入双向链表排队
// mode:Node.EXCLUSIVE 代表互斥锁
private Node addWaiter(Node mode) {
// 将当前线程封装为 Node 结点
Node node = new Node(Thread.currentThread(), mode);
// pred 指向尾结点
Node pred = tail;
// pred 不为空,代表链表中已经有结点在排队了
if (pred != null) {
// 分为三步将新结点挂到链表末端
// 1. 当前结点的 prev 指向尾结点
node.prev = pred;
// 2. 以 CAS 的方式,将 tail 指向当前结点
if (compareAndSetTail(pred, node)) {
// 将 pred 的 next 指向当前结点
pred.next = node;
// 返回当前结点
return node;
}
}
// 若 CAS 失败或者 pred 为 null,则以死循环的方式,保证当前线程挂到双向链表末尾
enq(node);
return node;
}
enq()
private Node enq(final Node node) {
for (;;) {
// 再次拿到尾节点
Node t = tail;
// 如果尾节点为空,则构建一个伪头节点作为 head 和 tail。
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 同 addWaiter 中的逻辑
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueued()
判断当前线程是否还能再次尝试获取锁资源,如果不能再次获取,或者尝试获取但没获取到,则将当前线程挂起。
// 当前线程没有获取到锁资源,并且到AQS排队之后
// lock() 方法不用考虑中断。tryLock() 和 lockinterruptibly() 方法中才需要考虑。
final boolean acquireQueued(final Node node, int arg) {
// 是否获取锁失败,true:失败 false:成功
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 获取当前结点的上一个结点
final Node p = node.predecessor();
// 若前继结点是伪头结点,则尝试获取锁
if (p == head && tryAcquire(arg)) {
// 获取锁资源成功
// 将当前结点作为伪头结点,将 Node 的 thread 属性设置为 null
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 没资格竞争锁,或者有资格但是没竞争到锁
// 基于当前结点的前继结点来判断当前结点是否可以挂起
if (shouldParkAfterFailedAcquire(p, node) &&
// 基于 UNSAFE.park() 将当前线程挂起,等待被唤醒
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire()
// 是否可以挂起当前线程
// 已知:当前结点需要前继结点来唤醒它
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
// 表示 pred 结点的后续结点需要唤醒(-1)
if (ws == Node.SIGNAL)
return true;
// pred 结点是取消状态(1)
if (ws > 0) {
// 往前找,直到找到一个结点的状态不等于 1 的结点。
do {
// 作为当前结点的上一个结点
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
// 将 pred 的 next 指向当前结点。
pred.next = node;
} else {
// pred 结点的状态不是 1 和 -1(0、-2、-3),此处不涉及 -2 和 -3。所以状态是 0。
// 将 pred 结点的状态改为 -1,下次循环时进入`if (ws == Node.SIGNAL)`判断
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
Node 的状态
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;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3
// ...
}
tryLock()
没有参数的 tryLock() 方法不区分公平和非公平锁。
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
tryLock(long timeout, TimeUnit unit)
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
tryAcquireNanos()
public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
// 若线程的中断标记位被修改为 true,则抛异常
// acquireQueued() 方法中有修改 interrupted 属性。
if (Thread.interrupted())
throw new InterruptedException();
// 若 tryAcquire 获取锁成功,直接返回 true
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
doAcquireNanos()
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
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 true;
}
// 计算竞争锁剩余的时间
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
// 若剩余时间大于1000纳秒则挂起
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
// 若线程被唤醒,则判断是中断唤醒还是时间到了唤醒
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
// Node 在排队过程中被中断了
cancelAcquire(node);
}
}
cancelAcquire()
cancelAcquire() 可以将当前结点从 AQS 链表中删除。主要流程如下:
- 将当前结点的线程设置为 null
- 往前找到有效结点 pred
- 将当前结点的状态设置为 1,代表取消
- 将当前结点脱离 AQS 队列,分为 3 种情况:当前结点是尾结点/不是伪头结点的后继结点/伪头结点的后继结点
private void cancelAcquire(Node node) {
if (node == null)
return;
// 1. 线程设置为 null
node.thread = null;
// 2. 往前找有效结点并赋值给 pred
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// 3. 拿到 pred 的 next
Node predNext = pred.next;
// 4. 当前结点的状态设置为取消
node.waitStatus = Node.CANCELLED;
// 若当前 node 是尾结点,将尾结点指向 pred
if (node == tail && compareAndSetTail(node, pred)) {
// 将 pred 的 next 设置为 null
compareAndSetNext(pred, predNext, null);
} else {
// 当前结点不是尾结点或者 compareAndSetTail 失败
int ws;
// 当前结点不是伪头结点的后继结点
if (pred != head &&
// pred 结点的状态不为 -1 且不是取消状态(1),则通过 CAS 操作将 pred 的状态修改为 -1
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
// 校验 pred 结点的线程,避免并发情况下,pred 变成取消结点或者头结点
pred.thread != null) {
// 进入条件判断说明 pred 是有效结点,可以唤醒后面的结点
Node next = node.next;
// node 的 next 不为 null,且不是取消结点,则将 pred 的 next 指向当前结点的 next
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
// pred == head,当前结点是伪头结点的后继结点
// 唤醒后面的结点
unparkSuccessor(node);
}
// 当前结点的 next 指向当前结点
node.next = node; // help GC
}
}
lockInterruptibly()
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
acquireInterruptibly(int arg)
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
doAcquireInterruptibly(int arg)
// 拿不到锁资源就一直等,直到锁资源释放后被唤醒或者被中断唤醒
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();
}
} finally {
if (failed)
cancelAcquire(node);
}
parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
// 可以确认当前挂起的线程是被中断唤醒的还是被正常唤醒的
// 被中断唤醒返回 true,正常唤醒返回 false
return Thread.interrupted();
}
释放锁流程
释放锁不区分公平锁和非公平锁。
unlock()
public void unlock() {
sync.release(1);
}
release(int arg)
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
// 若 head 的状态不为 0(说明 head 的状态等于 -1,head 只有 0 和 -1 两种状态)
// 说明 AQS 队列中有排队的线程,且线程已经挂起。
if (h != null && h.waitStatus != 0)
// 唤醒排队线程
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease(int releases)
protected final boolean tryRelease(int releases) {
// state - 1
int c = getState() - releases;
// 判断当前持有锁的线程是不是当前线程
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
// 当前锁资源是否完全释放了(主要是因为可重入,重入几次就要释放几次)
boolean free = false;
if (c == 0) {
// 若 state - 1 == 0
free = true;
// 将持有锁的线程置为 null
setExclusiveOwnerThread(null);
}
// 将 state 更新为 c
setState(c);
// 若锁资源完全释放了,返回 true;否则返回 false。
return free;
}
unparkSuccessor(Node node)
从后往前找离 head 最近的有效节点并唤醒。
private void unparkSuccessor(Node node) {
// 拿到头结点的状态
int ws = node.waitStatus;
// 头结点小于 0 的情况只有 -1
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 先基于 CAS 操作,将头结点的状态从 -1 改为 0
// 头结点的后继结点
Node s = node.next;
// 若后继结点取消了(s.waitStatus > 0 就是 1)
if (s == null || s.waitStatus > 0) {
s = null;
// 从后往前找到一个与 head 最近的有效结点,可以避免跳过某个新加入的结点。(从后往前找的原因可以看设置 tail 节点的流程)
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
// 唤醒结点
LockSupport.unpark(s.thread);
}