目录
ReentrantLock
(可重入锁)和synchronized
是Java中实现线程同步的两种核心机制,
一、核心功能与特性
对比维度 |
synchronized |
ReentrantLock |
实现方式 |
关键字,由JVM底层实现(C++代码) |
类( |
可重入性 |
支持(同一线程可多次获取同一把锁,自动记录重入次数) |
支持(通过 |
锁释放 |
自动释放(同步块/方法执行完毕或异常时) |
手动释放(必须在 |
锁类型 |
非公平锁(默认),无法切换为公平锁 |
可通过构造函数指定公平/非公平锁( |
中断响应 |
不支持(线程获取锁时,无法被中断,会一直阻塞) |
支持(通过 |
超时获取 |
不支持(获取不到锁会一直阻塞) |
支持( |
条件变量 |
不支持(仅能通过对象的 |
支持(通过 |
锁状态查询 |
无法直接查询 |
可查询( |
锁实现机制 |
|
|
二、性能对比
- JDK 6之前:
synchronized
性能较差(存在重量级锁的优化不足),ReentrantLock
性能更优。 - JDK 6及之后:JVM对
synchronized
进行了大量优化(如偏向锁、轻量级锁、锁消除、锁粗化等),两者性能差异不大,在多数场景下synchronized
甚至更优(JVM对其优化更深入)。
三、使用方式
场景 |
推荐选择 |
原因 |
简单的同步代码块/方法 |
|
语法简洁,无需手动释放锁,不易出错,JVM优化充分。 |
需要公平锁机制 |
|
可通过构造函数指定公平锁,保证线程获取锁的顺序(按等待时间排序)。 |
需要中断等待锁的线程 |
|
|
需要超时获取锁 |
|
|
需要多条件等待/唤醒 |
|
|
需要查询锁状态 |
|
提供多种方法查询锁状态,便于调试和监控。 |
资源竞争激烈的高并发场景 |
两者均可(视情况) |
JDK 6+后性能接近, |
四、典型代码示例
1. synchronized
的使用
// 同步方法
public synchronized void syncMethod() {
// 同步逻辑
}
// 同步代码块
public void syncBlock() {
synchronized (this) {
// 同步逻辑
}
}
2. ReentrantLock
的使用
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private final ReentrantLock lock = new ReentrantLock(); // 非公平锁(默认)
// private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
public void lockMethod() {
lock.lock(); // 获取锁
try {
// 同步逻辑
} finally {
lock.unlock(); // 必须在finally中释放锁
}
}
// 超时获取锁
public boolean tryLockWithTimeout() throws InterruptedException {
if (lock.tryLock(1, TimeUnit.SECONDS)) { // 1秒超时
try {
// 同步逻辑
return true;
} finally {
lock.unlock();
}
}
return false;
}
}
五、总结
synchronized
的优势:语法简单、自动释放锁、不易出错、JVM优化成熟,适合大多数基础同步场景。ReentrantLock
的优势:功能更灵活(公平锁、中断、超时、多条件),适合复杂同步场景,需手动管理锁释放(注意finally
块)。