Java并发编程:深入解析ReentrantLock与ReadWriteLock
在Java 5.0引入ReentrantLock之前,开发者主要依赖synchronized内置锁。显示锁提供了更精细的控制能力,包括公平性选择、超时获取及中断处理,而ReadWriteLock通过分离读写权限进一步优化了高并发读取场景的性能。
ReentrantLock核心概述
ReentrantLock是可重入的互斥锁,它显式地管理加锁与解锁过程。相比内置锁,它具备更高级的特性:
- 可重入性:线程可以重复获得已持有的锁。
- 灵活性:支持公平锁与非公平锁、定时锁、有条件锁(Condition)及可轮询锁。
- 活跃度优势:能有效避免部分死锁场景,提供更细粒度的锁控制。
Lock接口关键方法
ReentrantLock实现了java.util.concurrent.locks.Lock接口,其核心方法定义了锁的行为模式:
public interface Lock {
// 阻塞直到获得锁或被中断
void lock();
// 尝试获取锁,若失败立即返回false
boolean tryLock();
// 尝试在指定时间内获取锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 释放锁
void unlock();
// 创建绑定到此锁的条件变量
Condition newCondition();
}
第1步:基础加锁与解锁操作
为了确保锁总是被正确释放,防止因异常导致死锁,必须遵循“先加锁,后try-finally解锁”的标准范式。
- 目的:安全地保护共享资源。
- 动作:在
try块前调用lock(),在finally块中调用unlock()。 - 参数:
lock实例。 - 完成状态:代码执行结束后锁状态重置为未锁定,允许其他线程获取。
Lock lock = new ReentrantLock();
lock.lock();
try {
// 更新对象状态或执行临界区代码
} finally {
// 无论是否发生异常,都必须解锁
lock.unlock();
}
ReentrantLock的高级特性
1. 公平性与非公平性
构造函数允许指定锁的公平性策略,默认通常为非公平锁。
- 公平锁(Fair Lock):严格遵循FIFO(先进先出)队列顺序分配锁,请求较早的线程优先获得,无插队现象。
- 非公平锁(Unfair Lock):新请求线程可能直接在锁可用时获得所有权,无需等待队列。虽然可能导致某些线程饥饿,但在大多数场景下吞吐量更高,因为减少了线程挂起和唤醒的上下文切换开销。
2. 轮询与定时获取锁
通过tryLock()方法实现,提供容错机制以应对潜在的锁竞争冲突。
- tryLock():如果当前锁空闲则获取并返回true;否则立即返回false。这允许线程在不无限期阻塞的情况下决定后续逻辑。
- tryLock(time, unit):在指定时间内尝试获取锁。若在超时时间内未获得,返回false。
这种机制有助于打破由多个线程按不同顺序申请多个锁而导致的死锁循环。
3. 可中断的锁获取
使用lockInterruptibly()方法可以在等待锁的过程中响应线程的中断信号。这对于需要优雅退出长时间等待的场景至关重要,避免了传统lock()无法被中断的问题。
ReadWriteLock读写锁应用
ReentrantLock是互斥锁,同一时间仅允许一个线程持有。而ReadWriteLock接口暴露了两个锁对象:readLock和writeLock。
- 读锁(共享锁):只要没有线程持有写锁,多个线程可以同时获取读锁。
- 写锁(排他锁):一旦有线程获取了读锁或写锁,其他试图获取写锁的线程将被阻塞。
该接口定义如下:
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
最常见的实现是ReentrantReadWriteLock,同样支持公平与非公平模式。它特别适用于读多写少的数据结构,能显著提升并发读取性能。
第2步:构建线程安全的读写Map
以下示例展示了如何利用ReadWriteLock创建一个线程安全的映射容器,实现高并发读取与串行写入。
- 准备阶段:实例化
ReentrantReadWriteLock及其对应的读/写锁。 - 读取操作:在
get方法中获取readLock,确保多线程可同时进入。 - 写入操作:在
put方法中获取writeLock,确保数据修改时的排他性。 - 资源释放:所有操作均在
finally块中释放相应锁。
public class ReadWriteMap<K, V> {
private final Map<K, V> map;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
public ReadWriteMap(Map<K, V> map) {
this.map = map;
}
// 读取操作:允许多个线程同时访问
public V get(K key) {
readLock.lock();
try {
return map.get(key);
} finally {
readLock.unlock();
}
}
// 写入操作:互斥访问
public void put(K key, V value) {
writeLock.lock();
try {
map.put(key, value);
} finally {
writeLock.unlock();
}
}
}
以上就是Java并发编程中显示锁ReentrantLock和读写锁ReadWriteLock的详细内容,更多关于Java高级特性及并发控制的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













