Understanding ReentrantLock: Reentrant Locks, Differences from synchronized, and Internal Implementation
This article explains the concept of reentrant locks in Java, compares ReentrantLock with the synchronized keyword, and provides a detailed walkthrough of the internal source code for lock acquisition, fairness policies, and lock release using AQS, complete with code examples.
ReentrantLock is a Reentrant Lock
A reentrant lock allows a thread that already holds the lock to acquire it again without blocking; the lock maintains a counter that is incremented on each successful acquisition.
Both synchronized and ReentrantLock are reentrant because each lock object tracks a lock count, incrementing it when the same thread calls lock() again.
Differences Between ReentrantLock and synchronized
synchronizedis a language-level construct that automatically handles lock acquisition and release, while ReentrantLock is a Java class that requires explicit calls to lock() and unlock(). synchronized always blocks until the lock is obtained; ReentrantLock can attempt to acquire the lock with try‑lock semantics and supports both fair and non‑fair acquisition policies. ReentrantLock also provides additional features such as interruptible lock acquisition and condition variables.
lock & NonfairSync & FairSync Details
lock
The entry point for acquiring a lock is the lock() method:
public void lock() {
sync.lock();
}Here, sync is a static inner class of ReentrantLock that extends AbstractQueuedSynchronizer (AQS). The Sync class has two concrete subclasses: NonfairSync and FairSync.
NonfairSync
NonfairSync attempts to acquire the lock immediately using a CAS operation; if it fails, it falls back to the standard AQS acquire() path.
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
// 重写 Sync 的 lock 方法
final void lock() {
// 先不管其他,上来就先 CAS 操作,尝试抢占一下锁
if (compareAndSetState(0, 1))
// 如果抢占成功,就获得了锁
setExclusiveOwnerThread(Thread.currentThread());
else
// 没有抢占成功,调用 acquire() 方法,走里面的逻辑
acquire(1);
}
// 重写了 AQS 的 tryAcquire 方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}FairSync
FairSync enforces FIFO ordering; it does not attempt a CAS grab and directly calls acquire(1), which respects the waiting queue.
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
// 重写 Sync 的 lock 方法
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
// 重写了 Sync 的 tryAcquire 方法
protected final boolean tryAcquire(int acquires) {
// 获取当前执行的线程
final Thread current = Thread.currentThread();
// 获取 state 的值
int c = getState();
// 在无锁状态下
if (c == 0) {
// 没有前驱节点且替换 state 的值成功时
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
// 保存当前获得锁的线程,下次再来时,就不需要尝试竞争锁,直接重入即可
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
// 如果是同一个线程来获得锁,直接增加重入次数即可
int nextc = c + acquires;
// nextc 小于 0 ,抛异常
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
// 获取锁成功
return true;
}
// 获取锁失败
return false;
}
}Summary of NonfairSync and FairSync
NonfairSync: tries a fast CAS grab regardless of waiting threads; if it fails, it enqueues the thread via AQS.
FairSync: respects FIFO order, always enqueues the thread, and only grants the lock when it is at the head of the queue.
ReentrantLock Lock Acquisition
NonfairSync.lock()
The lock acquisition logic in NonfairSync is:
final void lock() {
// 不管别的,上来就先 CAS 操作,尝试抢占一下锁
if (compareAndSetState(0, 1))
// 如果抢占成功,就获得了锁
setExclusiveOwnerThread(Thread.currentThread());
else
// 没有抢占成功,调用 acquire() 方法,走里面的逻辑
acquire(1);
}AQS.acquire()
The core AQS method that attempts to acquire the lock or enqueues the thread:
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}If tryAcquire fails, the thread is wrapped in a Node and added to the wait queue.
AQS.addWaiter
private Node addWaiter(Node mode) {
// 生成该线程所对应的 Node 节点
Node node = new Node(Thread.currentThread(), mode);
// 将 Node 插入队列中
Node pred = tail;
if (pred != null) {
node.prev = pred;
// 使用 CAS 操作,如果成功就返回
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果 pred == null 或者 CAS 操作失败,则调用 enq 方法再次自旋插入
enq(node);
return node;
}AQS.acquireQueued
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
// 如果 Node 的前驱节点 p 是 head,说明 Node 是第二个节点,那么它就可以尝试获取锁
if (p == head && tryAcquire(arg)) {
// 如果锁获取成功,则将 head 指向自己
setHead(node);
// 锁获取成功之后,将 next 指向 null,即将节点 p 从队列中移除
p.next = null; // help GC
failed = false;
return interrupted;
}
// 节点进入等待队列后,调用 shouldParkAfterFailedAcquire 或者 parkAndCheckInterrupt 方法
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}shouldParkAfterFailedAcquire
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 获取 pred 的状态
int ws = pred.waitStatus;
// 如果状态为 SIGNAL,那么直接返回 true,挂起线程即可
if (ws == Node.SIGNAL)
return true;
// 如果状态大于 0,说明线程被取消
if (ws > 0) {
// 从链表中移除被 cancel 的线程,使用循环来保证移除成功
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// CAS 操作修改 pred 节点状态为 SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
// 不需要挂起线程
return false;
}ReentrantLock Unlock
Unlocking delegates to sync.release(1), which ultimately calls AQS's release method.
public void unlock() {
sync.release(1);
}AQS.release()
public final boolean release(int arg) {
// 如果释放锁成功
if (tryRelease(arg)) {
// 获取 AQS 队列的头结点
Node h = head;
// 如果头结点不为空,且状态 != 0
if (h != null && h.waitStatus != 0)
// 调用 unparkSuccessor 方法唤醒后续节点
unparkSuccessor(h);
return true;
}
return false;
}ReentrantLock.tryRelease()
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 判断当前线程是否为获取到锁的线程,如果不是则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 次数为 0,说明释放锁完毕
if (c == 0) {
free = true;
// 释放之后,当前线程置为 null
setExclusiveOwnerThread(null);
}
// 更新重入次数
setState(c);
return free;
}AQS.unparkSuccessor
private void unparkSuccessor(Node node) {
// 获取当前节点的状态
int ws = node.waitStatus;
// 如果节点状态小于 0,则进行 CAS 操作设置为 0
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
// 获取当前节点的下一个节点 s
Node s = node.next;
// 如果 s 为空,则从尾部节点开始,或者 s.waitStatus > 0,说明节点被取消
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)
// next 节点不为空,直接唤醒即可
LockSupport.unpark(s.thread);
}The article concludes with a note that the analysis is based on JDK 1.8 source code.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
