在Java并发编程中,当业务场景存在读多写少的特征时,使用普通的互斥锁如ReentrantLock或synchronized会导致所有读操作互斥执行,无法发挥多线程的优势。ReentrantReadWriteLock是Java并发包中提供的读写锁实现,通过读锁共享、写锁互斥的特性,能够有效提升并发读场景下的性能。

ReentrantReadWriteLock核心机制
ReentrantReadWriteLock内部维护了一对锁,分别是读锁和写锁,其特性如下:
- 读锁是共享锁,同一时间可以有多个线程同时持有读锁,执行读操作
- 写锁是排他锁,同一时间只能有一个线程持有写锁,执行写操作时,会阻塞其他所有读线程和写线程
- 支持锁的重入,读线程可以重入获取读锁,写线程可以重入获取写锁,同时写线程获取写锁后也可以重入获取读锁(锁降级)
- 遵循写锁优先的原则,当有线程等待获取写锁时,新到来的读线程会被阻塞,避免写线程长时间等待
基础使用示例
下面通过一个简单的缓存类示例,展示ReentrantReadWriteLock的基本使用方式:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockCache<K, V> {
// 存储缓存数据
private final Map<K, V> cache = new HashMap<>();
// 创建读写锁实例
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
// 获取读锁
private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
// 获取写锁
private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
// 读操作,使用读锁
public V get(K key) {
readLock.lock();
try {
// 执行读操作,多个线程可以同时进入该区域
System.out.println(Thread.currentThread().getName() + " 获取读锁,执行读操作");
return cache.get(key);
} finally {
readLock.unlock();
}
}
// 写操作,使用写锁
public void put(K key, V value) {
writeLock.lock();
try {
// 执行写操作,同一时间只有一个线程可以进入该区域
System.out.println(Thread.currentThread().getName() + " 获取写锁,执行写操作");
cache.put(key, value);
} finally {
writeLock.unlock();
}
}
}
性能提升原理分析
ReentrantReadWriteLock提升并发读性能的核心原因在于读锁的共享特性,我们可以通过对比普通互斥锁和读写锁的执行效率来理解:
普通互斥锁的执行逻辑
假设有10个读线程和1个写线程,普通互斥锁下所有线程串行执行,总耗时为10次读操作耗时 + 1次写操作耗时,读操作之间无法并行。
ReentrantReadWriteLock的执行逻辑
同样10个读线程和1个写线程,只要写线程不在执行,10个读线程可以同时获取读锁并行执行读操作,总耗时接近单次读操作耗时 + 1次写操作耗时,读操作的并行度大幅提升。
我们可以通过一个简单的测试代码验证这个效果:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ReadWriteLockTest {
public static void main(String[] args) throws InterruptedException {
ReadWriteLockCache<String, String> cache = new ReadWriteLockCache<>();
cache.put("test", "value");
ExecutorService executor = Executors.newFixedThreadPool(10);
long start = System.currentTimeMillis();
// 提交10个读任务
for (int i = 0; i < 10; i++) {
executor.submit(() -> {
cache.get("test");
try {
// 模拟读操作耗时
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.SECONDS);
long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start) + "毫秒");
}
}
上述代码中10个读线程的读操作可以并行执行,总耗时接近100毫秒,如果使用普通互斥锁,总耗时会接近1000毫秒。
使用注意事项
虽然ReentrantReadWriteLock能提升读性能,但使用时需要注意以下问题:
- 锁降级:写线程获取写锁后,可以获取读锁,然后释放写锁,实现锁降级,但读锁无法升级为写锁,避免死锁
- 避免写线程饥饿:由于ReentrantReadWriteLock默认是非公平的,可能导致写线程长时间等待,如果需要避免这种情况,可以创建公平模式的读写锁:
new ReentrantReadWriteLock(true),但公平模式会降低整体吞吐量 - 读锁不支持条件变量:读锁调用
newCondition()会抛出UnsupportedOperationException,只有写锁支持条件变量 - 适用场景:仅适合读多写少的场景,如果写操作占比很高,读写锁的性能可能不如普通互斥锁,因为写锁的获取和释放开销更大
总结
ReentrantReadWriteLock通过分离读锁和写锁,利用读锁共享的特性,在读多写少的并发场景下能够大幅提升读操作的并行度,从而提升整体性能。使用时需要结合业务场景判断是否适用,同时注意锁的重入、降级、公平性等问题,避免引入新的并发问题。合理运用该工具,能够有效优化Java并发程序的读性能表现。
ReentrantReadWriteLockJava并发读写锁读性能优化修改时间:2026-07-24 12:12:30