Java多线程同步方法ReentrantLock显式锁实现方式
一、基本使用方式
-
显式获取与释放锁
- 声明锁对象:通常使用
final
修饰ReentrantLock
实例,避免外部修改导致锁状态异常。 - 加锁与解锁:需手动调用
lock()
和unlock()
方法,且unlock()
必须放在finally
块中,确保异常时锁仍能释放。public class SharedData {private final ReentrantLock lock = new ReentrantLock();public void accessData() {lock.lock(); // 显式获取锁try {// 临界区代码} finally {lock.unlock(); // 显式释放锁}} }
- 关键点:若在
lock()
和try
之间插入代码(如日志记录),需确保不会抛出异常,否则unlock()
可能未执行,导致锁泄漏。
- 声明锁对象:通常使用
-
锁的可重入性
- 同一线程可多次获取同一把锁,释放次数需与获取次数匹配。
public void nestedLock() {lock.lock();try {lock.lock(); // 允许重入try { ... }finally { lock.unlock(); } // 第二次释放} finally { lock.unlock(); } // 第一次释放 }
- 此特性避免了线程在递归或嵌套调用中因重复加锁导致的死锁。
- 同一线程可多次获取同一把锁,释放次数需与获取次数匹配。
二、公平性与非公平性
-
构造函数参数
- 通过
ReentrantLock(boolean fair)
指定公平策略:- 公平锁(
fair=true
):按请求顺序分配锁,减少线程饥饿,但可能降低吞吐量。 - 非公平锁(默认):允许插队获取锁,性能更高,但可能引发饥饿。
- 公平锁(
- 通过
-
适用场景
- 高并发场景下优先使用非公平锁;需保证公平性(如资源分配)时选择公平锁。
三、高级特性
-
尝试加锁与超时机制
- 尝试非阻塞加锁:
tryLock()
方法立即返回是否成功获取锁,避免线程阻塞。 - 带超时的尝试加锁:
tryLock(long timeout, TimeUnit unit)
在指定时间内尝试获取锁,超时后返回false
。if (lock.tryLock(1, TimeUnit.SECONDS)) {try { ... }finally { lock.unlock(); } }
- 尝试非阻塞加锁:
-
条件变量(Condition)
- 通过
newCondition()
创建多个条件队列,实现多条件等待与通知。 - 示例:生产者-消费者模式中,不同条件(如“数据已满”和“数据为空”)可分别管理。
ReentrantLock lock = new ReentrantLock(); Condition notEmpty = lock.newCondition(); Condition notFull = lock.newCondition(); // 生产者线程 lock.lock(); try {while (queue.size() >= CAPACITY) {notFull.await(); // 等待队列不满}queue.add(item);notEmpty.signal(); // 通知消费者 } finally { lock.unlock(); } // 消费者线程 lock.lock(); try {while (queue.isEmpty()) {notEmpty.await(); // 等待队列非空}item = queue.remove();notFull.signal(); // 通知生产者 } finally { lock.unlock(); }
- 相比
synchronized
的wait/notify
,Condition
支持更细粒度的线程协作。
- 通过
四、性能优化与注意事项
- 性能对比
- 在低竞争场景下,
synchronized
因JVM优化(如偏向锁)可能更快;高竞争场景下,ReentrantLock
的可控制性更优。 ReentrantLock
的显式锁释放机制减少了线程上下文切换的开销。
- 在低竞争场景下,
- 常见问题
- 锁泄漏:忘记调用
unlock()
会导致其他线程永久阻塞,需严格使用finally
块。 - 死锁:嵌套锁需确保获取顺序一致,或使用超时机制避免无限等待。
- 锁泄漏:忘记调用
五、与synchronized
的对比
特性 | ReentrantLock | synchronized |
---|---|---|
加锁方式 | 显式(需手动调用lock/unlock ) | 隐式(编译器自动处理) |
锁粒度 | 可指定任意对象锁 | 方法或代码块(基于对象实例) |
公平性 | 支持公平与非公平模式 | 仅非公平 |
条件变量 | 支持多条件队列 | 仅单条件(wait/notify ) |
性能 | 高竞争场景更优 | 低竞争场景更优 |
六、适用场景
- 需要精细控制锁行为:如超时加锁、多条件等待。
- 高并发系统:通过非公平锁优化吞吐量。
- 复杂业务逻辑:可重入性避免递归调用中的死锁风险。
通过合理使用ReentrantLock
的显式锁机制,开发者可在多线程环境中实现高效、灵活的同步控制。