ReentrantLock 原理及业务场景详解
·
一、ReentrantLock 核心架构
1. 类继承关系
ReentrantLock
├── Lock 接口
├── Serializable 接口
└── 内部类:
├── Sync (extends AbstractQueuedSynchronizer)
│ ├── NonfairSync
│ └── FairSync
└── ConditionObject
2. 核心组件
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync; // 核心同步器
abstract static class Sync extends AbstractQueuedSynchronizer {
// AQS 的实现
}
static final class NonfairSync extends Sync {}
static final class FairSync extends Sync {}
}
二、AQS(AbstractQueuedSynchronizer)原理
1. AQS 核心数据结构
public abstract class AbstractQueuedSynchronizer {
// 同步状态
private volatile int state;
// 等待队列(CLH变种)
private transient volatile Node head;
private transient volatile Node tail;
static final class Node {
volatile int waitStatus; // 等待状态
volatile Node prev; // 前驱节点
volatile Node next; // 后继节点
volatile Thread thread; // 等待线程
Node nextWaiter; // Condition队列链接
}
}
2. CLH队列工作流程
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ ThreadA │ -> │ ThreadB │ -> │ ThreadC │ -> │ ThreadD │
│ (持有锁) │ │ (等待) │ │ (等待) │ │ (等待) │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
三、公平锁 vs 非公平锁
1. 实现差异对比
// 非公平锁获取锁的流程
final void lock() {
if (compareAndSetState(0, 1)) // 尝试直接抢锁
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// 公平锁获取锁的流程
final void lock() {
acquire(1); // 直接进入队列排队
}
// 公平锁的tryAcquire方法
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 关键区别:检查队列中是否有前驱节点
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 重入逻辑...
}
2. 性能对比
| 场景 | 公平锁 | 非公平锁 |
|---|---|---|
| 高竞争场景 | 吞吐量低 | 吞吐量高 |
| 线程切换 | 频繁 | 较少 |
| 饥饿问题 | 无 | 可能存在 |
| 实际应用 | 较少 | 默认策略 |
四、可重入性实现原理
1. 重入计数机制
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 第一次获取锁
if (!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;
}
// 释放锁时的重入计数
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { // 完全释放锁
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
五、Condition 条件变量
1. Condition 原理
public class ConditionObject implements Condition {
private transient Node firstWaiter; // 条件队列头
private transient Node lastWaiter; // 条件队列尾
// 等待方法
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter(); // 添加到条件队列
int savedState = fullyRelease(node); // 完全释放锁
int interruptMode = 0;
while (!isOnSyncQueue(node)) { // 不在同步队列中
LockSupport.park(this); // 阻塞
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 被唤醒后重新获取锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
}
2. 等待队列 vs 条件队列
同步队列(CLH队列):
┌───────┐ ┌───────┐ ┌───────┐
│ NodeA │←→│ NodeB │←→│ NodeC │
└───────┘ └───────┘ └───────┘
竞争锁 等待锁 等待锁
条件队列(单向链表):
┌───────┐ ┌───────┐ ┌───────┐
│ NodeX │→ │ NodeY │→ │ NodeZ │→ null
└───────┘ └───────┘ └───────┘
等待条件1 等待条件1 等待条件2
六、业务场景使用详解
场景1:线程安全的缓存系统
public class Cache<K, V> {
private final Map<K, V> cache = new HashMap<>();
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
private final int maxSize;
public Cache(int maxSize) {
this.maxSize = maxSize;
}
public V get(K key) {
lock.lock();
try {
// 等待直到缓存不为空
while (cache.isEmpty()) {
notEmpty.await();
}
return cache.get(key);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} finally {
lock.unlock();
}
}
public void put(K key, V value) {
lock.lock();
try {
// 等待直到缓存不满
while (cache.size() >= maxSize) {
notFull.await();
}
cache.put(key, value);
notEmpty.signal(); // 通知等待的消费者
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public boolean remove(K key) {
lock.lock();
try {
boolean removed = cache.remove(key) != null;
if (removed) {
notFull.signal(); // 通知生产者可以继续添加
}
return removed;
} finally {
lock.unlock();
}
}
}
场景2:数据库连接池
public class ConnectionPool {
private final LinkedList<Connection> pool = new LinkedList<>();
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
private final int maxSize;
private int activeCount = 0;
public ConnectionPool(int maxSize) {
this.maxSize = maxSize;
}
public Connection getConnection(long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
long nanos = unit.toNanos(timeout);
lock.lockInterruptibly(); // 可中断的获取锁
try {
// 等待直到有可用连接
while (pool.isEmpty() && activeCount >= maxSize) {
if (nanos <= 0) {
throw new TimeoutException();
}
nanos = notEmpty.awaitNanos(nanos);
}
Connection conn;
if (!pool.isEmpty()) {
conn = pool.removeFirst();
} else {
conn = createNewConnection();
activeCount++;
}
return conn;
} finally {
lock.unlock();
}
}
public void releaseConnection(Connection conn) {
lock.lock();
try {
if (conn.isValid()) {
pool.addLast(conn);
notEmpty.signal(); // 通知等待的线程
} else {
activeCount--;
notFull.signal(); // 通知可以创建新连接
}
} finally {
lock.unlock();
}
}
}
场景3:读写锁模拟(业务特定场景)
public class ReadWriteResource {
private final ReentrantLock lock = new ReentrantLock();
private final Condition readCondition = lock.newCondition();
private final Condition writeCondition = lock.newCondition();
private int readers = 0;
private boolean writing = false;
private int writeRequests = 0;
public void readLock() throws InterruptedException {
lock.lock();
try {
while (writing || writeRequests > 0) {
readCondition.await();
}
readers++;
} finally {
lock.unlock();
}
}
public void readUnlock() {
lock.lock();
try {
readers--;
if (readers == 0) {
writeCondition.signal(); // 通知写线程
}
} finally {
lock.unlock();
}
}
public void writeLock() throws InterruptedException {
lock.lock();
try {
writeRequests++;
while (writing || readers > 0) {
writeCondition.await();
}
writeRequests--;
writing = true;
} finally {
lock.unlock();
}
}
public void writeUnlock() {
lock.lock();
try {
writing = false;
// 优先唤醒等待的写线程
if (writeRequests > 0) {
writeCondition.signal();
} else {
readCondition.signalAll(); // 唤醒所有读线程
}
} finally {
lock.unlock();
}
}
}
场景4:批量任务处理器
public class BatchTaskProcessor {
private final ReentrantLock lock = new ReentrantLock();
private final Condition batchReady = lock.newCondition();
private final List<Task> batch = new ArrayList<>();
private final int batchSize;
private volatile boolean shutdown = false;
public BatchTaskProcessor(int batchSize) {
this.batchSize = batchSize;
}
public void addTask(Task task) throws InterruptedException {
lock.lockInterruptibly();
try {
batch.add(task);
// 批量达到阈值时通知处理器
if (batch.size() >= batchSize) {
batchReady.signal();
}
} finally {
lock.unlock();
}
}
public void processBatch() {
lock.lock();
try {
// 等待批量任务准备就绪
while (batch.size() < batchSize && !shutdown) {
batchReady.await();
}
if (!batch.isEmpty()) {
List<Task> currentBatch = new ArrayList<>(batch);
batch.clear();
// 释放锁后再执行耗时操作
lock.unlock();
try {
executeBatch(currentBatch);
} finally {
lock.lock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
场景5:分布式锁的本地优化
public class LocalCacheWithStampedLock {
private final Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
private final ReentrantLock lock = new ReentrantLock();
private final Condition updateCondition = lock.newCondition();
private volatile long lastUpdateTime = 0;
private final long updateInterval;
public LocalCacheWithStampedLock(long updateInterval) {
this.updateInterval = updateInterval;
}
public Object get(String key) {
CacheEntry entry = cache.get(key);
// 乐观读:检查是否需要更新
if (entry == null ||
System.currentTimeMillis() - lastUpdateTime > updateInterval) {
// 尝试升级为写锁
return readWithUpdate(key);
}
return entry.getValue();
}
private Object readWithUpdate(String key) {
// 使用 tryLock 避免阻塞
if (lock.tryLock()) {
try {
// 双重检查
CacheEntry entry = cache.get(key);
if (entry == null ||
System.currentTimeMillis() - lastUpdateTime > updateInterval) {
// 执行更新
entry = loadFromDataSource(key);
cache.put(key, entry);
lastUpdateTime = System.currentTimeMillis();
updateCondition.signalAll(); // 通知其他等待线程
}
return entry.getValue();
} finally {
lock.unlock();
}
} else {
// 获取不到锁,使用旧值
CacheEntry entry = cache.get(key);
return entry != null ? entry.getValue() : null;
}
}
}
七、高级特性与最佳实践
1. 锁的降级模式
public class LockDowngradeExample {
private final ReentrantLock lock = new ReentrantLock();
private volatile boolean dataValid = false;
private Object data;
public void writeThenRead() {
lock.lock(); // 获取写锁
try {
// 写操作
data = fetchData();
dataValid = true;
// 锁降级:在写锁内获取读锁
lock.lock(); // 重入获取锁(读操作)
try {
// 读操作(仍在锁保护下)
processData(data);
} finally {
lock.unlock(); // 释放读锁(但写锁仍持有)
}
} finally {
lock.unlock(); // 释放写锁
}
}
}
2. 避免死锁的策略
public class DeadlockAvoidance {
private final ReentrantLock lock1 = new ReentrantLock();
private final ReentrantLock lock2 = new ReentrantLock();
public void method1() {
// 尝试获取锁,带有超时
if (lock1.tryLock(100, TimeUnit.MILLISECONDS)) {
try {
Thread.sleep(50); // 模拟工作
if (lock2.tryLock(100, TimeUnit.MILLISECONDS)) {
try {
// 成功获取两把锁
doWork();
} finally {
lock2.unlock();
}
} else {
// 获取第二把锁失败,回滚
rollback();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock1.unlock();
}
}
}
// 使用锁排序避免死锁
public void orderedLocking(Object obj1, Object obj2) {
int hash1 = System.identityHashCode(obj1);
int hash2 = System.identityHashCode(obj2);
ReentrantLock firstLock = hash1 < hash2 ? lock1 : lock2;
ReentrantLock secondLock = hash1 < hash2 ? lock2 : lock1;
firstLock.lock();
try {
secondLock.lock();
try {
doWork();
} finally {
secondLock.unlock();
}
} finally {
firstLock.unlock();
}
}
}
3. 性能监控与诊断
public class LockProfiler {
private final ReentrantLock lock = new ReentrantLock(true); // 公平锁用于调试
private final ThreadLocal<Long> lockStartTime = new ThreadLocal<>();
private final AtomicLong totalWaitTime = new AtomicLong();
private final AtomicInteger lockCount = new AtomicInteger();
public void performTask() {
long startWait = System.nanoTime();
lock.lock();
try {
long waitTime = System.nanoTime() - startWait;
totalWaitTime.addAndGet(waitTime);
lockCount.incrementAndGet();
lockStartTime.set(System.nanoTime());
// 执行业务逻辑
doBusinessLogic();
long holdTime = System.nanoTime() - lockStartTime.get();
logLockMetrics(waitTime, holdTime);
} finally {
lock.unlock();
}
}
public LockStats getStats() {
return new LockStats(
lockCount.get(),
totalWaitTime.get() / lockCount.get(),
lock.getQueueLength() // 获取等待队列长度
);
}
}
八、与 synchronized 的对比
1. 功能对比表
| 特性 | ReentrantLock | synchronized |
|---|---|---|
| 可重入性 | ✅ 支持 | ✅ 支持 |
| 公平锁 | ✅ 可选择 | ❌ 非公平 |
| 可中断 | ✅ lockInterruptibly() | ❌ 等待不可中断 |
| 尝试获取 | ✅ tryLock() | ❌ 不支持 |
| 超时机制 | ✅ tryLock(timeout) | ❌ 不支持 |
| 多条件变量 | ✅ 多个Condition | ❌ 单条件 |
| 锁绑定 | ❌ 无 | ✅ Object关联 |
| 性能 | JDK6+优化后相当 | JDK6+优化后相当 |
2. 选择建议
// 使用 synchronized 的情况:
// 1. 简单的同步场景
// 2. 需要与 wait/notify 配合
// 3. 不需要高级特性
// 使用 ReentrantLock 的情况:
// 1. 需要可中断的锁获取
// 2. 需要尝试获取锁(tryLock)
// 3. 需要公平锁
// 4. 需要多个条件变量
// 5. 需要锁的统计信息
九、常见问题与解决方案
Q1: 为什么 try-finally 中释放锁?
// ❌ 错误示例
lock.lock();
// 如果这里抛出异常,锁永远不会释放
doSomething();
lock.unlock();
// ✅ 正确示例
lock.lock();
try {
doSomething();
} finally {
lock.unlock(); // 确保锁被释放
}
Q2: 如何处理锁的嵌套?
public class NestedLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void outerMethod() {
lock.lock();
try {
innerMethod(); // 重入锁
} finally {
lock.unlock();
}
}
public void innerMethod() {
// 可重入,不会死锁
lock.lock();
try {
// 内层逻辑
} finally {
lock.unlock(); // 释放内层锁
}
}
}
Q3: 如何避免锁泄漏?
public class LockLeakPrevention {
private final ReentrantLock lock = new ReentrantLock();
private final Set<Thread> lockHolders = ConcurrentHashMap.newKeySet();
public void safeOperation() {
if (!lock.tryLock()) {
throw new IllegalStateException("锁被占用,可能发生泄漏");
}
try {
lockHolders.add(Thread.currentThread());
// 执行业务逻辑
doOperation();
} finally {
lockHolders.remove(Thread.currentThread());
lock.unlock();
}
}
public void detectLeak() {
if (lock.isLocked() && lockHolders.isEmpty()) {
// 检测到锁泄漏:锁被占用但没有记录持有者
handleLockLeak();
}
}
}
十、总结
ReentrantLock 核心优势:
-
灵活性:提供丰富的锁操作API
-
可扩展性:支持公平锁、条件变量等高级特性
-
诊断友好:可获取锁的状态信息
-
性能可控:在特定场景下可优化性能
适用场景:
-
需要精确控制锁获取顺序的业务
-
需要可中断锁操作的场景
-
实现复杂的同步策略(如读写锁、批量处理)
-
需要监控锁性能的应用
最佳实践:
-
始终在 try-finally 中释放锁
-
合理选择公平/非公平策略
-
优先使用 tryLock 避免死锁
-
监控锁的竞争情况,及时优化
更多推荐



所有评论(0)