ReentrantLock
的构造方法可以指定是否使用公平锁:
public ReentrantLock() {
this.sync = new NonfairSync(); // 默认是非公平锁
}
public ReentrantLock(boolean fair) {
this.sync = fair ? new FairSync() : new NonfairSync(); // 根据参数选择锁的类型
}
非公平锁 (NonfairSync
)
tryLock
方法
final boolean tryLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { // 锁未被占用
if (compareAndSetState(0, 1)) { // 尝试通过 CAS 获取锁
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) { // 当前线程已持有锁
setState(c + 1); // 增加重入计数
return true;
}
return false; // 获取失败
}
- 核心特点:
- 非公平锁允许线程插队,不需要检查等待队列是否有其他线程。
- 如果锁未被占用,通过 CAS 直接尝试获取。
- 如果锁已被当前线程持有,支持重入操作,直接增加
state
值。
lock
方法
final void lock() {
if (!initialTryLock()) { // 尝试快速获取锁
acquire(1); // 获取失败,则进入 AQS 队列
}
}
initialTryLock
方法
final boolean initialTryLock() {
Thread current = Thread.currentThread();
if (compareAndSetState(0, 1)) { // CAS 尝试获取锁
setExclusiveOwnerThread(current);
return true;
} else if (getExclusiveOwnerThread() == current) { // 当前线程已持有锁
setState(getState() + 1); // 增加重入计数
return true;
}
return false; // 获取失败
}
- 核心特点:
- 非公平锁先尝试快速获取锁,通过
initialTryLock
方法直接操作state
。 - 如果快速获取失败,进入 AQS 的等待队列,由 AQS 负责排队、阻塞和唤醒。
- 非公平锁先尝试快速获取锁,通过
acquire()
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) { selfInterrupt(); } }
解析
尝试获取锁:
- 首先调用
tryAcquire(arg)
尝试获取锁。- 非公平锁和公平锁都通过各自的
tryAcquire
方法实现不同的获取逻辑。加入等待队列:
- 如果
tryAcquire(arg)
返回false
,则调用addWaiter(Node.EXCLUSIVE)
方法将当前线程加入等待队列。- 等待队列是基于
AbstractQueuedSynchronizer
的Node
实现的 FIFO 结构。进入队列等待:
- 调用
acquireQueued
方法,在队列中等待唤醒并重新尝试获取锁。中断线程:
- 如果线程在等待过程中被中断,调用
selfInterrupt
方法设置线程的中断状态。tryAcquire(arg)方法
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 锁未被占用 if (compareAndSetState(0, acquires)) { // CAS 尝试获取锁 setExclusiveOwnerThread(current); // 设置当前线程为锁的持有者 return true; } } else if (current == getExclusiveOwnerThread()) { // 当前线程已持有锁 setState(c + acquires); // 增加重入次数 return true; } return false; // 获取失败 }
addWaiter
方法private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // 创建一个新的节点 Node pred = tail; // 获取队列尾节点 if (pred != null) { node.prev = pred; // 当前节点的前驱指向队尾 if (compareAndSetTail(pred, node)) { // CAS 设置新节点为队尾 pred.next = node; // 前驱节点的 next 指向当前节点 return node; } } enq(node); // 如果失败,调用 enq 方法完成入队 return node; }
- 功能:将当前线程封装成节点,并添加到等待队列的尾部。
- 快速入队:如果队列已初始化,通过 CAS 快速将新节点插入队列尾部。
- 完整入队:如果快速插入失败,调用
enq
方法完成完整的入队操作。
acquireQueued
方法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; // 断开前驱节点的 next 引用,帮助 GC failed = false; // 标记获取成功 return interrupted; // 返回中断状态 } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; // 如果需要挂起线程并检测到中断,标记中断状态 } } finally { if (failed) cancelAcquire(node); // 如果失败,从等待队列中移除节点 } }
unlock
方法
final void unlock() {
sync.release(1); // 调用 AQS 的 release 方法
}
AQS 的 release
方法
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 方法
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread()) {
throw new IllegalMonitorStateException(); // 非锁持有线程调用 unlock 抛出异常
}
boolean free = c == 0; // 判断锁是否完全释放
if (free) {
setExclusiveOwnerThread(null); // 清除持有线程
}
setState(c); // 更新锁的状态
return free;
}
- 核心特点:
- 调用线程必须是锁的持有者,否则抛出
IllegalMonitorStateException
。 - 减少
state
值(释放锁或减少重入计数),当state == 0
时完全释放锁。 - 如果有等待线程,唤醒队列中的第一个线程。
- 调用线程必须是锁的持有者,否则抛出
公平锁 (FairSync
)
tryLock
方法
同非公平锁的该方法
lock
方法
final void lock() {
acquire(1); // 调用 AQS 的 acquire 方法
}
- 核心特点:
- 公平锁的
lock
方法不支持快速获取锁。 - 直接调用 AQS 的
acquire
方法,将当前线程加入等待队列,由 AQS 负责管理线程的排队、阻塞和唤醒。
- 公平锁的
acquire方法跟非公平锁的一样,其中只是tryAcquire方法不一样
公平锁的tryAcquire方法:
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 锁未被占用 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { // 检查队列是否有前置线程,如果没有,CAS 尝试获取锁 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 当前线程已持有锁 setState(c + acquires); // 增加重入次数 return true; } return false; // 获取失败 }
unlock
方法
公平锁的 unlock
方法和非公平锁相同:
- 调用 AQS 的
release
方法尝试释放锁。 - 减少
state
值,当state == 0
时完全释放锁。 - 如果有等待线程,唤醒队列中的第一个线程。