概述
ReentrantLock是Java并发包java.util.concurrent.locks中的一个重要类,它提供了与synchronized关键字相同的基本行为和语义,但具有更强的功能。ReentrantLock基于AQS(AbstractQueuedSynchronizer)实现,支持重入、公平性选择、条件变量等高级特性。
主要特性
- 重入性:同一线程可以多次获取同一把锁
- 公平性:支持公平锁和非公平锁两种模式
- 可中断:支持响应中断的锁获取
- 超时机制:支持尝试获取锁的超时机制
- 条件变量:支持多个条件变量(Condition)
ReentrantLock基础概念
基本使用方式
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 临界区代码
System.out.println("执行业务逻辑");
} finally {
lock.unlock(); // 必须在finally中释放锁
}
}
// 可中断的锁获取
public void interruptibleMethod() throws InterruptedException {
lock.lockInterruptibly();
try {
// 业务逻辑
} finally {
lock.unlock();
}
}
// 尝试获取锁
public void tryLockMethod() {
if (lock.tryLock()) {
try {
// 业务逻辑
} finally {
lock.unlock();
}
} else {
System.out.println("获取锁失败");
}
}
}
构造函数
// 默认非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 可选择公平性
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
AQS核心原理深度解析
AQS简介
AbstractQueuedSynchronizer(AQS)是Java并发包的核心基础组件,为实现依赖于先进先出(FIFO)等待队列的阻塞锁和相关同步器提供了一个框架。
AQS的核心组件
1. state状态字段
// AQS中的核心状态字段
private volatile int state;
// 获取状态
protected final int getState() {
return state;
}
// 设置状态
protected final void setState(int newState) {
state = newState;
}
// CAS操作更新状态
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
在ReentrantLock中,state的含义:
state = 0:锁未被任何线程持有state = 1:锁被某个线程持有一次state = n:锁被某个线程重入n次
2. CLH队列
AQS使用CLH(Craig, Landin, and Hagersten)队列的变种来管理等待线程:
// AQS中的节点定义
static final class Node {
// 共享模式
static final Node SHARED = new Node();
// 独占模式
static final Node EXCLUSIVE = null;
// 节点状态常量
static final int CANCELLED = 1; // 节点被取消
static final int SIGNAL = -1; // 后继节点需要被唤醒
static final int CONDITION = -2; // 节点在条件队列中等待
static final int PROPAGATE = -3; // 共享模式下的传播
volatile int waitStatus; // 等待状态
volatile Node prev; // 前驱节点
volatile Node next; // 后继节点
volatile Thread thread; // 关联的线程
Node nextWaiter; // 条件队列中的下一个节点
}
3. 独占锁的获取流程
// AQS中的acquire方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
流程说明:
- 调用
tryAcquire()尝试获取锁 - 如果失败,调用
addWaiter()将当前线程加入等待队列 - 调用
acquireQueued()在队列中等待获取锁 - 如果在等待过程中被中断,则自我中断
ReentrantLock内部结构分析
类继承关系
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
// 内部抽象同步器
abstract static class Sync extends AbstractQueuedSynchronizer {
// 公共方法实现
}
// 非公平同步器
static final class NonfairSync extends Sync {
// 非公平锁实现
}
// 公平同步器
static final class FairSync extends Sync {
// 公平锁实现
}
}
Sync抽象类
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
// 抽象方法,由子类实现
abstract void lock();
// 非公平方式尝试获取锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 锁未被持有,尝试CAS获取
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
// 重入情况
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) {
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;
}
}
锁获取过程源码解析
非公平锁获取过程
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
// 非公平锁的lock方法
final void lock() {
// 直接尝试CAS获取锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // 调用AQS的acquire方法
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
公平锁获取过程
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1); // 直接调用AQS的acquire方法
}
// 公平锁的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;
}
}
else if (current == getExclusiveOwnerThread()) {
// 重入情况
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
acquireQueued方法详解
// AQS中的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; // help GC
failed = false;
return interrupted;
}
// 检查是否需要阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
锁释放过程源码解析
unlock方法
// ReentrantLock的unlock方法
public void unlock() {
sync.release(1);
}
// 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;
}
unparkSuccessor方法
// 唤醒后继节点
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
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); // 唤醒线程
}
重入机制实现原理
重入的实现
重入机制的核心在于:
- 使用
state字段记录重入次数 - 使用
exclusiveOwnerThread记录持有锁的线程
// 重入检查逻辑
if (current == getExclusiveOwnerThread()) {
// 当前线程已持有锁,增加重入次数
int nextc = c + acquires;
if (nextc < 0) // 防止溢出
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
重入示例
public class ReentrantExample {
private final ReentrantLock lock = new ReentrantLock();
public void method1() {
lock.lock(); // state: 0 -> 1
try {
System.out.println("method1");
method2(); // 重入调用
} finally {
lock.unlock(); // state: 1 -> 0
}
}
public void method2() {
lock.lock(); // state: 1 -> 2 (重入)
try {
System.out.println("method2");
} finally {
lock.unlock(); // state: 2 -> 1
}
}
}
公平锁vs非公平锁对比
实现差异
| 特性 | 公平锁 | 非公平锁 |
|---|---|---|
| 获取策略 | 严格按照FIFO顺序 | 允许"插队" |
| 性能 | 较低(需要检查队列) | 较高(直接尝试获取) |
| 吞吐量 | 较低 | 较高 |
| 延迟 | 较高但稳定 | 较低但不稳定 |
| 饥饿问题 | 不会发生 | 可能发生 |
关键代码差异
// 公平锁:需要检查队列
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
// 非公平锁:直接尝试获取
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
hasQueuedPredecessors方法
// 检查是否有前驱节点在等待
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
Condition条件变量详解
Condition接口
public interface Condition {
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
}
ConditionObject实现
// AQS内部的ConditionObject实现
public class ConditionObject implements Condition, java.io.Serializable {
private transient Node firstWaiter; // 条件队列头节点
private transient Node lastWaiter; // 条件队列尾节点
// await实现
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);
}
// signal实现
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first); // 唤醒第一个等待节点
}
}
生产者消费者示例
public class ProducerConsumer {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Object[] items = new Object[100];
private int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await(); // 队列满时等待
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal(); // 通知消费者
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await(); // 队列空时等待
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal(); // 通知生产者
return x;
} finally {
lock.unlock();
}
}
}
与synchronized全面对比
功能对比
| 特性 | ReentrantLock | synchronized |
|---|---|---|
| 锁的实现 | API层面 | JVM层面 |
| 性能 | 高并发下更优 | JDK6后优化显著 |
| 功能 | 功能丰富 | 功能简单 |
| 可中断性 | 支持 | 不支持 |
| 超时机制 | 支持 | 不支持 |
| 公平性 | 可选择 | 非公平 |
| 条件变量 | 支持多个 | 只有一个 |
| 重入性 | 支持 | 支持 |
| 异常处理 | 手动释放 | 自动释放 |
使用场景选择
选择ReentrantLock的场景:
- 需要可中断的锁获取
- 需要超时机制
- 需要公平锁
- 需要多个条件变量
- 需要尝试获取锁
选择synchronized的场景:
- 简单的同步需求
- 不需要高级功能
- 代码简洁性要求高
- JVM自动优化
性能测试对比
public class PerformanceTest {
private static final int THREAD_COUNT = 10;
private static final int ITERATIONS = 1000000;
// ReentrantLock测试
private final ReentrantLock lock = new ReentrantLock();
private int reentrantCounter = 0;
public void reentrantLockTest() {
lock.lock();
try {
reentrantCounter++;
} finally {
lock.unlock();
}
}
// synchronized测试
private int syncCounter = 0;
public synchronized void synchronizedTest() {
syncCounter++;
}
}
性能分析和使用场景
性能特点
- 低竞争场景:synchronized和ReentrantLock性能相近
- 高竞争场景:ReentrantLock通常表现更好
- 公平锁:性能比非公平锁低10-20%
- 条件变量:比Object.wait/notify更高效
适用场景
ReentrantLock适用场景
// 1. 需要可中断的锁获取
public void interruptibleTask() {
try {
lock.lockInterruptibly();
try {
// 长时间运行的任务
longRunningTask();
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
// 处理中断
Thread.currentThread().interrupt();
}
}
// 2. 需要超时机制
public boolean tryWithTimeout() {
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try {
// 业务逻辑
return true;
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
// 3. 需要公平性保证
public class FairQueue<T> {
private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
private final Queue<T> queue = new LinkedList<>();
public void enqueue(T item) {
lock.lock();
try {
queue.offer(item);
} finally {
lock.unlock();
}
}
}
常见面试题集锦
1. ReentrantLock与synchronized的区别?
答案要点:
- 实现层面:ReentrantLock是API层面,synchronized是JVM层面
- 功能丰富性:ReentrantLock支持可中断、超时、公平性、多条件变量
- 性能:高并发下ReentrantLock通常更优
- 使用复杂度:synchronized更简单,ReentrantLock需要手动释放
2. 什么是AQS?ReentrantLock如何基于AQS实现?
答案要点:
- AQS是AbstractQueuedSynchronizer,提供了同步器的基础框架
- 核心组件:state状态字段、CLH队列、独占/共享模式
- ReentrantLock通过继承AQS的Sync类实现
- 重写tryAcquire和tryRelease方法定义锁的获取和释放逻辑
3. 公平锁和非公平锁的实现差异?
答案要点:
- 公平锁:严格按照FIFO顺序,需要检查hasQueuedPredecessors()
- 非公平锁:允许"插队",直接尝试CAS获取锁
- 性能差异:非公平锁吞吐量更高,公平锁延迟更稳定
- 使用场景:公平锁适合对顺序敏感的场景
4. ReentrantLock如何实现重入?
答案要点:
- 使用state字段记录重入次数
- 使用exclusiveOwnerThread记录持有锁的线程
- 同一线程再次获取锁时,增加state计数
- 释放锁时,减少state计数,直到为0才真正释放
5. Condition的工作原理?
答案要点:
- Condition基于条件队列实现
- await()方法:释放锁,加入条件队列,阻塞等待
- signal()方法:将条件队列中的节点移到同步队列
- 支持多个条件变量,比Object.wait/notify更灵活
6. 如何避免死锁?
死锁问题分析
**对称锁死锁(Symmetric Lock Deadlock)**是最常见的死锁场景:
public class SymmetricLockDeadlockDemo {
private final ReentrantLock lock1 = new ReentrantLock();
private final ReentrantLock lock2 = new ReentrantLock();
// 线程1执行此方法
public void method1() {
lock1.lock();
try {
System.out.println("Thread1 acquired lock1");
Thread.sleep(100); // 模拟业务处理
lock2.lock(); // 尝试获取lock2,可能导致死锁
try {
System.out.println("Thread1 acquired lock2");
} finally {
lock2.unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock1.unlock();
}
}
// 线程2执行此方法
public void method2() {
lock2.lock();
try {
System.out.println("Thread2 acquired lock2");
Thread.sleep(100); // 模拟业务处理
lock1.lock(); // 尝试获取lock1,可能导致死锁
try {
System.out.println("Thread2 acquired lock1");
} finally {
lock1.unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock2.unlock();
}
}
}
活锁问题(Livelock)
活锁是另一种并发问题,线程不会被阻塞,但会不断改变状态以响应其他线程:
public class ReentrantLockLivelockDemo {
private final ReentrantLock lock1 = new ReentrantLock();
private final ReentrantLock lock2 = new ReentrantLock();
public void politeWorker1() {
while (true) {
lock1.lock();
try {
if (lock2.tryLock()) {
try {
// 成功获取两个锁,执行业务逻辑
System.out.println("Worker1 completed task");
break;
} finally {
lock2.unlock();
}
} else {
// 获取lock2失败,"礼貌地"释放lock1
System.out.println("Worker1 backing off");
}
} finally {
lock1.unlock();
}
// 短暂等待后重试
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
public void politeWorker2() {
while (true) {
lock2.lock();
try {
if (lock1.tryLock()) {
try {
// 成功获取两个锁,执行业务逻辑
System.out.println("Worker2 completed task");
break;
} finally {
lock1.unlock();
}
} else {
// 获取lock1失败,"礼貌地"释放lock2
System.out.println("Worker2 backing off");
}
} finally {
lock2.unlock();
}
// 短暂等待后重试
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
线程池死锁(Cyclic Thread Pool Deadlock)
线程池中的循环依赖也可能导致死锁,特别是当任务之间存在相互等待的情况:
public class CyclicThreadPoolDeadLockDemo {
private final ExecutorService executor1 = Executors.newFixedThreadPool(1);
private final ExecutorService executor2 = Executors.newFixedThreadPool(1);
public void demonstrateDeadlock() {
// 在executor1中提交任务,该任务会向executor2提交子任务并等待结果
Future<String> future1 = executor1.submit(() -> {
System.out.println("Task1 in executor1 started");
// 向executor2提交任务并等待结果
Future<String> future2 = executor2.submit(() -> {
System.out.println("Task2 in executor2 started");
// 再向executor1提交任务并等待结果 - 这里会死锁!
Future<String> future3 = executor1.submit(() -> {
return "Task3 result";
});
try {
return future3.get(); // 等待executor1中的任务完成
} catch (Exception e) {
throw new RuntimeException(e);
}
});
try {
return future2.get(); // 等待executor2中的任务完成
} catch (Exception e) {
throw new RuntimeException(e);
}
});
try {
String result = future1.get(5, TimeUnit.SECONDS);
System.out.println("Result: " + result);
} catch (TimeoutException e) {
System.err.println("Deadlock detected: Task timed out!");
} catch (Exception e) {
e.printStackTrace();
}
}
// 使用CompletableFuture避免死锁
public void avoidDeadlockWithCompletableFuture() {
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> {
System.out.println("Task1 started");
return "intermediate result";
}, executor1)
.thenComposeAsync(result -> {
System.out.println("Task2 started with: " + result);
return CompletableFuture.supplyAsync(() -> {
System.out.println("Task3 started");
return "final result";
}, executor1); // 可以安全地使用同一个executor
}, executor2);
try {
String result = future.get(5, TimeUnit.SECONDS);
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
public void cleanup() {
executor1.shutdown();
executor2.shutdown();
}
}
答案要点:
- 按顺序获取锁:始终以相同顺序获取多个锁
- 使用tryLock()避免无限等待:设置超时时间或立即返回
- 使用lockInterruptibly()支持中断:允许线程被中断
- 避免嵌套锁:减少锁的层次结构
- 使用超时机制:tryLock(timeout, TimeUnit)
- 检测死锁:使用JVM工具或编程方式检测
// 死锁预防:统一锁顺序
public class DeadlockPrevention {
private final ReentrantLock lock1 = new ReentrantLock();
private final ReentrantLock lock2 = new ReentrantLock();
// 方法1和方法2都按相同顺序获取锁
public void safeMethod1() {
acquireLocksInOrder();
}
public void safeMethod2() {
acquireLocksInOrder();
}
private void acquireLocksInOrder() {
lock1.lock();
try {
lock2.lock();
try {
// 业务逻辑
} finally {
lock2.unlock();
}
} finally {
lock1.unlock();
}
}
// 使用超时机制避免死锁
public boolean safeMethodWithTimeout() {
boolean acquired1 = false, acquired2 = false;
try {
acquired1 = lock1.tryLock(1, TimeUnit.SECONDS);
if (acquired1) {
acquired2 = lock2.tryLock(1, TimeUnit.SECONDS);
if (acquired2) {
// 业务逻辑
return true;
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (acquired2) lock2.unlock();
if (acquired1) lock1.unlock();
}
return false;
}
}
7. ReentrantLock的性能优化建议?
性能测试对比
以下是ReentrantLock与synchronized在不同场景下的性能对比:
public class LockPerformanceTest {
private final ReentrantLock reentrantLock = new ReentrantLock();
private final ReentrantLock fairLock = new ReentrantLock(true);
private final Object syncLock = new Object();
private volatile int counter = 0;
// 测试方法:ReentrantLock(非公平)
public void testReentrantLock(int iterations) {
for (int i = 0; i < iterations; i++) {
reentrantLock.lock();
try {
counter++;
} finally {
reentrantLock.unlock();
}
}
}
// 测试方法:ReentrantLock(公平)
public void testFairLock(int iterations) {
for (int i = 0; i < iterations; i++) {
fairLock.lock();
try {
counter++;
} finally {
fairLock.unlock();
}
}
}
// 测试方法:synchronized
public void testSynchronized(int iterations) {
for (int i = 0; i < iterations; i++) {
synchronized (syncLock) {
counter++;
}
}
}
// 性能测试结果(仅供参考,实际结果依赖于硬件和JVM):
// 单线程场景(1,000,000次操作):
// - synchronized: ~15ms
// - ReentrantLock(非公平): ~18ms
// - ReentrantLock(公平): ~25ms
//
// 高竞争场景(10个线程,每个100,000次操作):
// - synchronized: ~200ms
// - ReentrantLock(非公平): ~180ms
// - ReentrantLock(公平): ~350ms
}
优化策略详解
答案要点:
- 减少锁的持有时间
// ❌ 锁持有时间过长
public void inefficientMethod() {
lock.lock();
try {
// 耗时的计算
String result = expensiveCalculation();
// 网络IO操作
sendToRemoteService(result);
// 更新共享状态
updateSharedState(result);
} finally {
lock.unlock();
}
}
// ✅ 优化后:只在必要时持有锁
public void efficientMethod() {
// 耗时操作在锁外进行
String result = expensiveCalculation();
sendToRemoteService(result);
// 只在更新共享状态时持有锁
lock.lock();
try {
updateSharedState(result);
} finally {
lock.unlock();
}
}
- 减少锁的粒度
// ❌ 粗粒度锁
public class CoarseGrainedLocking {
private final ReentrantLock lock = new ReentrantLock();
private final Map<String, Object> data = new HashMap<>();
private final List<String> logs = new ArrayList<>();
public void updateData(String key, Object value) {
lock.lock();
try {
data.put(key, value);
} finally {
lock.unlock();
}
}
public void addLog(String message) {
lock.lock();
try {
logs.add(message);
} finally {
lock.unlock();
}
}
}
// ✅ 细粒度锁
public class FineGrainedLocking {
private final ReentrantLock dataLock = new ReentrantLock();
private final ReentrantLock logLock = new ReentrantLock();
private final Map<String, Object> data = new HashMap<>();
private final List<String> logs = new ArrayList<>();
public void updateData(String key, Object value) {
dataLock.lock();
try {
data.put(key, value);
} finally {
dataLock.unlock();
}
}
public void addLog(String message) {
logLock.lock();
try {
logs.add(message);
} finally {
logLock.unlock();
}
}
}
-
选择合适的公平性策略
- 非公平锁:吞吐量更高,适合高性能场景
- 公平锁:延迟更稳定,适合对响应时间敏感的场景
-
使用tryLock()避免阻塞
public boolean nonBlockingUpdate(String key, Object value) {
if (lock.tryLock()) {
try {
data.put(key, value);
return true;
} finally {
lock.unlock();
}
}
return false; // 获取锁失败,可以执行备选逻辑
}
// 带超时的tryLock
public boolean timedUpdate(String key, Object value) {
try {
if (lock.tryLock(100, TimeUnit.MILLISECONDS)) {
try {
data.put(key, value);
return true;
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
- 考虑使用读写锁ReadWriteLock
- 读多写少的场景下,ReadWriteLock性能更优
- 允许多个读线程同时访问,提高并发度
最佳实践和注意事项
1. 正确的使用模式
// ✅ 正确的使用方式
public void correctUsage() {
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock(); // 必须在finally中释放
}
}
// ❌ 错误的使用方式
public void incorrectUsage() {
lock.lock();
// 业务逻辑
lock.unlock(); // 可能因为异常导致锁无法释放
}
2. 避免常见陷阱
// ❌ 忘记释放锁
public void forgotUnlock() {
lock.lock();
if (someCondition) {
return; // 忘记释放锁
}
lock.unlock();
}
// ❌ 重复释放锁
public void duplicateUnlock() {
lock.lock();
try {
// 业务逻辑
lock.unlock(); // 错误:在try块中释放
} finally {
lock.unlock(); // 重复释放,抛出IllegalMonitorStateException
}
}
// ✅ 正确处理异常情况
public void correctExceptionHandling() {
lock.lock();
try {
// 可能抛出异常的业务逻辑
riskyOperation();
} catch (Exception e) {
// 处理异常
handleException(e);
} finally {
lock.unlock(); // 确保锁被释放
}
}
3. 性能优化建议
// 1. 减少锁的持有时间
public void optimizedLocking() {
// 准备工作在锁外进行
Object data = prepareData();
lock.lock();
try {
// 只在必要时持有锁
updateSharedState(data);
} finally {
lock.unlock();
}
// 后续处理在锁外进行
postProcess();
}
// 2. 使用tryLock避免阻塞
public boolean nonBlockingOperation() {
if (lock.tryLock()) {
try {
// 业务逻辑
return true;
} finally {
lock.unlock();
}
}
return false; // 获取锁失败,执行备选方案
}
// 3. 合理选择公平性
public class OptimizedService {
// 高吞吐量场景使用非公平锁
private final ReentrantLock highThroughputLock = new ReentrantLock(false);
// 需要顺序保证的场景使用公平锁
private final ReentrantLock fairOrderLock = new ReentrantLock(true);
}
4. 监控和调试
基础监控方法
public class LockMonitor {
private final ReentrantLock lock = new ReentrantLock();
public void monitorLock() {
System.out.println("队列长度: " + lock.getQueueLength());
System.out.println("是否有线程等待: " + lock.hasQueuedThreads());
System.out.println("是否被当前线程持有: " + lock.isHeldByCurrentThread());
System.out.println("持有计数: " + lock.getHoldCount());
System.out.println("是否公平锁: " + lock.isFair());
System.out.println("是否被锁定: " + lock.isLocked());
}
}
高级监控工具
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class AdvancedLockMonitor {
private final ReentrantLock lock = new ReentrantLock();
private final AtomicLong lockAcquisitions = new AtomicLong(0);
private final AtomicLong lockContentions = new AtomicLong(0);
private final AtomicLong totalWaitTime = new AtomicLong(0);
private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
public void lockWithMonitoring() {
long startTime = System.nanoTime();
boolean acquired = false;
try {
// 尝试立即获取锁
if (lock.tryLock()) {
acquired = true;
lockAcquisitions.incrementAndGet();
} else {
// 记录竞争
lockContentions.incrementAndGet();
// 阻塞获取锁
lock.lock();
acquired = true;
lockAcquisitions.incrementAndGet();
long waitTime = System.nanoTime() - startTime;
totalWaitTime.addAndGet(waitTime);
}
// 执行业务逻辑
doWork();
} finally {
if (acquired) {
lock.unlock();
}
}
}
public void printStatistics() {
System.out.println("=== 锁统计信息 ===");
System.out.println("总获取次数: " + lockAcquisitions.get());
System.out.println("竞争次数: " + lockContentions.get());
System.out.println("竞争率: " +
String.format("%.2f%%",
(double) lockContentions.get() / lockAcquisitions.get() * 100));
System.out.println("平均等待时间: " +
TimeUnit.NANOSECONDS.toMicros(totalWaitTime.get() / Math.max(1, lockContentions.get())) + "μs");
// 当前锁状态
System.out.println("当前队列长度: " + lock.getQueueLength());
System.out.println("当前持有线程: " +
(lock.isLocked() ? "Thread-" + Thread.currentThread().getId() : "无"));
}
private void doWork() {
// 模拟业务逻辑
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
死锁检测工具
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class DeadlockDetector {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
public void startDeadlockDetection() {
scheduler.scheduleAtFixedRate(this::checkForDeadlocks, 0, 5, TimeUnit.SECONDS);
}
private void checkForDeadlocks() {
long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
if (deadlockedThreads != null && deadlockedThreads.length > 0) {
System.err.println("检测到死锁!涉及线程数: " + deadlockedThreads.length);
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads);
for (ThreadInfo threadInfo : threadInfos) {
System.err.println("死锁线程: " + threadInfo.getThreadName());
System.err.println("线程状态: " + threadInfo.getThreadState());
System.err.println("阻塞在: " + threadInfo.getLockName());
System.err.println("持有锁: " + threadInfo.getLockOwnerId());
StackTraceElement[] stackTrace = threadInfo.getStackTrace();
for (StackTraceElement element : stackTrace) {
System.err.println("\t" + element.toString());
}
System.err.println();
}
// 可以选择记录日志、发送告警等
handleDeadlock(threadInfos);
}
}
private void handleDeadlock(ThreadInfo[] deadlockedThreads) {
// 处理死锁的策略
// 1. 记录详细日志
// 2. 发送告警通知
// 3. 尝试中断某些线程(谨慎使用)
System.err.println("正在处理死锁情况...");
}
public void shutdown() {
scheduler.shutdown();
}
}
JVM参数监控
在生产环境中,可以使用以下JVM参数来监控锁的行为:
# 启用锁竞争监控
-XX:+UseBiasedLocking
-XX:+PrintGCDetails
-XX:+PrintConcurrentLocks
# 使用JFR(Java Flight Recorder)监控
-XX:+FlightRecorder
-XX:StartFlightRecording=duration=60s,filename=lock-monitoring.jfr
# 启用详细的同步统计
-XX:+PrintGCApplicationStoppedTime
-XX:+TraceBiasedLocking
使用JConsole和VisualVM
// 为了便于JConsole监控,可以实现MBean接口
public interface LockMonitorMBean {
long getLockAcquisitions();
long getLockContentions();
double getContentionRate();
long getAverageWaitTime();
int getCurrentQueueLength();
}
public class LockMonitorMBeanImpl implements LockMonitorMBean {
private final AdvancedLockMonitor monitor;
public LockMonitorMBeanImpl(AdvancedLockMonitor monitor) {
this.monitor = monitor;
}
@Override
public long getLockAcquisitions() {
return monitor.lockAcquisitions.get();
}
@Override
public long getLockContentions() {
return monitor.lockContentions.get();
}
@Override
public double getContentionRate() {
long acquisitions = getLockAcquisitions();
return acquisitions > 0 ? (double) getLockContentions() / acquisitions * 100 : 0;
}
@Override
public long getAverageWaitTime() {
long contentions = getLockContentions();
return contentions > 0 ?
TimeUnit.NANOSECONDS.toMicros(monitor.totalWaitTime.get() / contentions) : 0;
}
@Override
public int getCurrentQueueLength() {
return monitor.lock.getQueueLength();
}
}
总结
ReentrantLock作为Java并发编程中的重要工具,是理解Java并发机制的关键。通过本文的深入分析,我们全面了解了其设计原理、实现细节和最佳实践。
核心特性回顾
-
可重入性(Reentrant)
- 同一线程可以多次获取同一把锁
- 通过state字段记录重入次数
- 避免了线程自己阻塞自己的问题
-
公平性选择(Fairness)
- 非公平锁:性能更高,但可能导致线程饥饿
- 公平锁:保证FIFO顺序,延迟更稳定
- 根据业务需求选择合适的策略
-
可中断性(Interruptible)
lockInterruptibly()支持响应中断- 避免线程无限期等待
- 提供更好的程序控制能力
-
超时机制(Timeout)
tryLock(timeout)支持带超时的锁获取- 防止死锁和长时间阻塞
- 提供降级处理机制
-
条件变量(Condition)
- 提供比wait/notify更强大的线程协调机制
- 支持多个条件队列
- 精确控制线程的唤醒
技术实现要点
AQS框架的巧妙设计
// AQS的核心思想
- state字段:表示同步状态(锁状态 + 重入次数)
- CLH队列:管理等待线程的FIFO队列
- 模板方法:tryAcquire/tryRelease由子类实现
- CAS操作:保证状态变更的原子性
关键源码理解
- 非公平锁:直接尝试CAS,失败后进入队列
- 公平锁:先检查队列,再尝试获取
- 重入机制:通过exclusiveOwnerThread和state计数实现
- 释放过程:递减state,为0时真正释放锁
性能特点分析
| 场景 | synchronized | ReentrantLock(非公平) | ReentrantLock(公平) |
|---|---|---|---|
| 单线程 | 最优 | 略慢 | 最慢 |
| 低竞争 | 优秀 | 相当 | 较慢 |
| 高竞争 | 较慢 | 优秀 | 慢 |
| 功能性 | 基础 | 丰富 | 丰富 |
最佳实践总结
✅ 推荐做法
// 1. 标准的锁使用模式
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock();
}
// 2. 使用tryLock避免死锁
if (lock.tryLock(100, TimeUnit.MILLISECONDS)) {
try {
// 业务逻辑
} finally {
lock.unlock();
}
} else {
// 降级处理
}
// 3. 合理的锁粒度
private final ReentrantLock dataLock = new ReentrantLock();
private final ReentrantLock logLock = new ReentrantLock();
❌ 避免的陷阱
// 1. 忘记释放锁
lock.lock();
// 业务逻辑
// 忘记unlock() - 导致死锁
// 2. 锁粒度过粗
synchronized(this) {
expensiveCalculation(); // 耗时操作不应在锁内
updateSharedState();
}
// 3. 对称锁死锁
// Thread1: lock1 -> lock2
// Thread2: lock2 -> lock1
面试重点梳理
高频问题
-
ReentrantLock与synchronized的区别?
- 功能:ReentrantLock更丰富(公平性、中断、超时)
- 性能:场景相关,高竞争下ReentrantLock可能更优
- 使用:synchronized更简单,ReentrantLock更灵活
-
什么是AQS?ReentrantLock如何基于AQS实现?
- AQS是同步器框架,提供状态管理和队列管理
- ReentrantLock通过继承AQS,实现tryAcquire/tryRelease
- 利用state字段和CLH队列完成锁的获取和释放
-
公平锁和非公平锁的区别?
- 获取策略:公平锁检查队列,非公平锁直接尝试
- 性能影响:非公平锁吞吐量更高,公平锁延迟更稳定
- 适用场景:根据业务对公平性和性能的要求选择
深度问题
-
ReentrantLock的重入是如何实现的?
- exclusiveOwnerThread记录持有线程
- state字段记录重入次数
- 每次重入递增state,释放时递减
-
如何避免死锁?
- 统一锁顺序
- 使用tryLock超时机制
- 避免嵌套锁
- 使用死锁检测工具
学习建议
进阶路径
- 源码阅读:深入理解AQS框架设计
- 实践应用:在项目中合理使用ReentrantLock
- 性能调优:学会监控和优化锁的性能
- 扩展学习:了解其他AQS实现(Semaphore、CountDownLatch等)
相关技术
- StampedLock:Java 8引入的高性能读写锁
- LockSupport:更底层的线程阻塞和唤醒工具
- CompletableFuture:异步编程,减少锁的使用
- Actor模型:无锁并发编程思想
结语
ReentrantLock不仅是一个实用的并发工具,更是理解Java并发编程精髓的窗口。通过掌握其原理和最佳实践,我们能够:
- 写出更安全、高效的并发代码
- 深入理解Java并发框架的设计思想
- 在面试中展现扎实的并发编程功底
- 为学习更高级的并发技术打下基础
并发编程是一门艺术,需要在理论学习和实践应用中不断提升。希望本文能够帮助你在Java并发编程的道路上更进一步!
文章标签
冬眠
博主专注于技术、阅读与思考。在这里记录学习、思考与生活。
