可重入锁
ReentrantLock
1、实现 Lock 接口
public class ReentrantLock implements Lock, java.io.Serializable
2、内部维护 Sync
private final Sync sync;
3、Sync 继承 AbstractQueuedSynchronizer
abstract static class Sync extends AbstractQueuedSynchronizer
4、Sync 的两个实现
(1)公平锁
static final class FairSync extends Sync
(2)非公平锁
static final class NonfairSync extends Sync
(3)默认为非公平锁实现
public ReentrantLock() {
sync = new NonfairSync();
}
NonfairSync 加锁成功
public void lock() {
sync.lock();
}
final void lock() {
//没有竞争,CAS将state从0改为1
if (compareAndSetState(0, 1))
//exclusiveOwnerThread为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
NonfairSync 加锁失败
public void lock() {
sync.lock();
}
final void lock() {
//已加锁,CAS修改失败
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//进入acquire
acquire(1);
}
public final void acquire(int arg) {
if (
//此时state已经是1,tryAcquire结果仍然失败
!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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) {
// 尝试用 cas 获得, 这里体现了非公平性: 不去检查 AQS 队列
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果已经获得了锁, 线程还是当前线程, 表示发生了锁重入
else if (current == getExclusiveOwnerThread()) {
// state++
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 获取失败, 回到调用处
return false;
}
private Node addWaiter(Node mode) {
// 将当前线程关联到一个 Node 对象上, 模式为独占模式
Node node = new Node(Thread.currentThread(), mode);
// 如果 tail 不为 null, cas 尝试将 Node 对象加入 AQS 队列尾部
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
// 双向链表
pred.next = node;
return node;
}
}
// 尝试将 Node 加入 AQS
enq(node);
return node;
}
1、addWaiter
(1)构造 Node 双向链表
(2)Node 的 waitStatus 状态,其中 0 为默认正常状态,-1 表示需要唤醒其后继节点
(3)Node 创建为懒惰初始化
(4)首次创建链表时:第一个 Node 为 Dummy(哑元)或哨兵,用来占位,并不关联线程,第二个 Node 关联当前线程
(5)双向链表的末尾 Node,其 waitStatus 总为 0
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
// 还没有, 设置 head 为哨兵节点(不对应线程,状态为 0)
if (compareAndSetHead(new Node())) {
tail = head;
}
} else {
// cas 尝试将 Node 对象加入 AQS 队列尾部
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
2、第一轮循环
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取当前Node的前驱节点p
final Node p = node.predecessor();
//若p为头节点,即node为队列的第二位,则再次tryAcquire获取锁,此时state为1,仍失败
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (
//第一次shouldParkAfterFailedAcquire:将前驱节点,即head的waitStatus改为-1,若返回false,进入第二轮循环,继续竞争
shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 获取上一个节点的状态
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) {
// 上一个节点都在阻塞, 则自己也阻塞
return true;
}
// > 0 表示取消状态
if (ws > 0) {
// 上一个节点取消, 那么重构删除前面所有取消的节点, 返回到外层循环重试
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 这次还没有阻塞
// 但下次如果重试不成功, 则需要阻塞,这时需要设置上一个节点状态为 Node.SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
3、第二轮循环
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取当前Node的前驱节点p
final Node p = node.predecessor();
//若p为头节点,即node为队列的第二位,则再次tryAcquire获取锁,此时state为1,仍失败
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (
//第二次shouldParkAfterFailedAcquire:前驱Node的waitStatus已经是-1,若返回true,则进入阻塞
shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
4、是否需要 unpark,由当前节点的前驱节点,是否 waitStatus == Node.SIGNAL 决定,而不是本节点的 waitStatus 决定
private final boolean parkAndCheckInterrupt() {
//调用park阻塞当前线程
LockSupport.park(this);
return Thread.interrupted();
}
4、多个线程经竞争失败
NonfairSync 释放锁成功
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//尝试释放锁
if (tryRelease(arg)) {
//队列头节点
Node h = head;
if (
//队列不为 null
h != null &&
//waitStatus == Node.SIGNAL 才需要 unpark
h.waitStatus != 0)
// unpark AQS 中等待的线程
unparkSuccessor(h);
return true;
}
return false;
}
1、公平锁、非公平锁,释放锁都是相同的 tryRelease 实现
protected final boolean tryRelease(int releases) {
// state--
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 支持锁重入, 只有 state 减为 0, 才释放成功
if (c == 0) {
free = true;
//exclusiveOwnerThread设置为null,表示没有线程占用锁
setExclusiveOwnerThread(null);
}
//state设置为0,表示释放锁
setState(c);
return free;
}
2、setExclusiveOwnerThread、setState 先后次序
(1)state 为 volatile
private volatile int state;
(2)exclusiveOwnerThread 并非 volatile
private transient Thread exclusiveOwnerThread;
(3)setState 后会加入写屏障,保证在写屏障之前,setExclusiveOwnerThread 的改动,都同步到主存当中,对其他线程可见
private void unparkSuccessor(Node node) {
//获取node的waitStatus
int ws = node.waitStatus;
// 如果状态为 Node.SIGNAL,尝试重置状态为 0
if (ws < 0) {
//允许失败
compareAndSetWaitStatus(node, ws, 0);
}
// 找到需要 unpark 的节点, 但本节点从 AQS 队列中脱离, 是由唤醒节点完成的
Node s = node.next;
// 不考虑已取消的节点, 从 AQS 队列从后至前找到队列最前面需要 unpark 的节点
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.unpark(s.thread);
}
3、unparkSuccessor
(1)找到队列中,离 head 最近的一个、没取消的 Node,unpark 恢复其运行
(2)其 Node 关联的线程,在 acquire -> acquireQueued -> parkAndCheckInterrupt -> LockSupport.park 被唤醒,返回
private final boolean parkAndCheckInterrupt() {
//线程在此被阻塞
LockSupport.park(this);
//被park的线程,需要interrupted,返回打断标记(true),并清除打断标记,即设置为false
return Thread.interrupted();
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取node的前驱节点
final Node p = node.predecessor();
//p为head,node尝试获取锁,state为0,成功
if (p == head && tryAcquire(arg)) {
//将新head设置为node
setHead(node);
//原head的next指针设置为null,即与node断开关联
p.next = null; // help GC,原head因为从链表断开,而可被垃圾回收
failed = false;
//返回true
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
//parkAndCheckInterrupt返回true,进入if块
parkAndCheckInterrupt())
//interrupted设置为true,进入下一轮循环
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void setHead(Node node) {
//将head设置为node
head = node;
//断开node与线程关联
node.thread = null;
//node的prev指针设置为null,即与原head断开关联
node.prev = null;
}
NonfairSync 释放锁失败
1、此时,有其它线程竞争(该线程不在阻塞队列),且被其先占用 exclusiveOwnerThread
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取node的前驱节点
final Node p = node.predecessor();
//p为head,node尝试获取锁,state为-1,tryAcquire失败
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
//parkAndCheckInterrupt返回true,进入if块
parkAndCheckInterrupt())
//interrupted设置为true,进入下一轮循环
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
2、重新进入 shouldParkAfterFailedAcquire、parkAndCheckInterrupt,被阻塞
NonfairSync 可重入原理
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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) {
//尝试使用CAS获得锁,体现非公平性: 不检查AQS队列
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果已经获得了锁, 线程还是当前线程, 表示发生了锁重入
else if (current == getExclusiveOwnerThread()) {
// state++
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
// state--
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 支持锁重入, 只有 state 减为 0, 才释放成功
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
ReentrantLock 可打断原理
1、不可打断模式:即使它被打断,仍会驻留在 AQS 队列中,一直要等到获得锁后,才能继续运行,只是打断标记设置为 true
private final boolean parkAndCheckInterrupt() {
// 如果打断标记已经是 true, 则 park 会失效
LockSupport.park(this);
// interrupted 会清除打断标记
return Thread.interrupted();
}
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;
failed = false;
// 还是需要获得锁后, 才能返回打断状态
return interrupted;
}
if (
shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()
) {
// 如果是因为 interrupt 被唤醒, 返回打断状态为 true
interrupted = true;
}
}
} finally {
if (failed)
cancelAcquire(node);
}
}
public final void acquire(int arg) {
if (
!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
) {
// 如果打断状态为 true
selfInterrupt();
}
}
static void selfInterrupt() {
// 重新产生一次中断
Thread.currentThread().interrupt();
}
2、可打断模式
public final void acquireInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 如果没有获得到锁,进入doAcquireInterruptibly
if (!tryAcquire(arg))
doAcquireInterruptibly(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()) {
//在 park 过程中如果被 interrupt,此时抛出异常,而不会再次进入 for (;;)
throw new InterruptedException();
}
}
} finally {
if (failed)
cancelAcquire(node);
}
}
FairSync 实现原理
final void lock() {
acquire(1);
}
public final void acquire(int arg) {
if (
!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
) {
selfInterrupt();
}
}
// 与非公平锁主要区别在于 tryAcquire 方法的实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (
// 先检查 AQS 队列中是否有前驱节点, 没有才去竞争
!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;
}
return false;
}
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return
// h != t 时,表示队列中有 Node
h != t &&
(
// (s = h.next) == null,表示队列中只有一个Node,即不与线程关联的head
(s = h.next) == null ||
// 或队列中第二个Node,所关联的线程不是当前线程
s.thread != Thread.currentThread()
);
}
条件变量实现原理
1、每个条件变量,对应着一个等待队列,其实现类为 ConditionObject
2、await 流程
public final void await() throws InterruptedException {
//如果线程已经被标记为中断,则抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//创建新Node到条件变量的等待队列(单向链表)
Node node = addConditionWaiter();
//释放锁,该线程在阻塞队列(双向链表)中的节点也已经被移除
//持有锁的线程可能发生锁重入,需要完全释放锁
long savedState = fullyRelease(node);
//这里会将线程挂起,除非线程节点被移到AQS的阻塞队列,或是线程被外部中断
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
//调用park,在条件变量队列阻塞
LockSupport.park(this);
//检查是否是由于被中断而唤醒,如果是,则跳出循环
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//在阻塞队列中尝试获取锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//节点已经在AQS阻塞队列中,与Condition的等待队列联系断开
//对于SIGNAL唤醒的线程而言,SIGNAL时除了将节点移到阻塞队列,同时也清空了node.nextWaiter
//而对于中断唤醒的线程而言,只是将节点移到阻塞队列,并没有清空node.nextWaiter(因为此时线程不持有锁,操作等待线程并非线程安全)
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//根据interruptMode决定是否需要抛出异常
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
private Node addConditionWaiter() {
Node t = lastWaiter;
//删除队列链表中,所有已取消的Node
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
//创建一个关联当前线程的新Node,状态为-2,添加至队列尾部
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
final long fullyRelease(Node node) {
boolean failed = true;
try {
//获取state计数值
long savedState = getState();
//清空state
if (release(savedState)) {
failed = false;
//返回0
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
public final boolean release(long arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒后继节点,进行竞争
unparkSuccessor(h);
return true;
}
return false;
}
3、signal 流程
public final void signal() {
//检查调用signal的线程,是否为锁的持有者
if (!isHeldExclusively())
//非锁的持有者,则抛出异常
throw new IllegalMonitorStateException();
//获取条件变量的第一个Node
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
//断开first与条件变量的关联
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (
//将一个节点从条件队列,转移到AQS队列。如果成功返回true
//可能的失败原因:被打断、超时等,从而放弃对锁的竞争
!transferForSignal(first) &&
//若失败,获取条件变量的下一个Node,进入下一轮唤醒循环
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
//尝试将被唤醒线程的Node,state从-2改为0
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
//修改失败,说明该节点已被取消,则寻找条件变量下一个节点
return false;
//修改成功,将node加入到AQS队列末尾,成功返回node的前驱节点p
Node p = enq(node);
//获取p的waitStatus
int ws = p.waitStatus;
if (
//表示p被取消
ws > 0 ||
//因为双向链表末尾Node的waitStatus必定为0,所以需要将其改为-1
!compareAndSetWaitStatus(p, ws, Node.SIGNAL))
//p被取消,或修改p失败,则直接唤醒node
LockSupport.unpark(node.thread);
return true;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战