🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐➕关注👀是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝+关注👀欢迎留言讨论
🔥🔥🔥(源码获取 + 调试运行 + 问题答疑)🔥🔥🔥 有兴趣可以联系我
🔥🔥🔥 文末有往期免费源码,直接领取获取(无删减,无套路)
我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。
🔥🔥🔥(免费,无删减,无套路):java swing管理系统源码 程序 代码 图形界面(11套)」
链接:https://pan.quark.cn/s/784a0d377810
提取码:见文章末尾
🔥🔥🔥(免费,无删减,无套路): Python源代码+开发文档说明(23套)」
链接:https://pan.quark.cn/s/1d351abbd11c
提取码:见文章末尾
🔥🔥🔥(免费,无删减,无套路):计算机专业精选源码+论文(26套)」
链接:https://pan.quark.cn/s/8682a41d0097
提取码:见文章末尾
🔥🔥🔥(免费,无删减,无套路):Java web项目源码整合开发ssm(30套)
链接:https://pan.quark.cn/s/1c6e0826cbfd
提取码:见文章末尾
🔥🔥🔥(免费,无删减,无套路):「在线考试系统源码(含搭建教程)」
链接:https://pan.quark.cn/s/96c4f00fdb43
提取码:见文章末尾
引言:读写锁的必要性
在多线程编程中,我们常常面临这样的困境:如何在高并发读取的场景下,既保证数据的一致性,又不牺牲系统性能?传统的互斥锁(如synchronized)虽然安全,但在读多写少的场景下显得力不从心。这正是ReentrantReadWriteLock大显身手的时刻。
ReentrantReadWriteLock核心概念
读写锁的基本规则
ReentrantReadWriteLock遵循三个基本原则:
-
读-读不互斥:多个线程可以同时持有读锁
-
读-写互斥:写锁持有期间,所有读锁和写锁请求都会被阻塞
-
写-写互斥:同一时刻只能有一个线程持有写锁
类结构概览
public class ReentrantReadWriteLock implements ReadWriteLock {
private final ReadLock readerLock;
private final WriteLock writerLock;
private final Sync sync;
public ReentrantReadWriteLock() {
this(false); // 默认非公平模式
}
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
}
基本使用模式
典型应用场景
public class ThreadSafeDataContainer {
private final Map<String, Object> data = new HashMap<>();
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
// 读操作 - 使用读锁
public Object get(String key) {
readLock.lock();
try {
return data.get(key);
} finally {
readLock.unlock();
}
}
// 写操作 - 使用写锁
public void put(String key, Object value) {
writeLock.lock();
try {
data.put(key, value);
} finally {
writeLock.unlock();
}
}
// 复合操作 - 使用写锁
public Object putIfAbsent(String key, Object value) {
writeLock.lock();
try {
Object current = data.get(key);
if (current == null) {
data.put(key, value);
}
return current;
} finally {
writeLock.unlock();
}
}
}
锁的降级:一个高级特性
锁降级是ReentrantReadWriteLock的一个重要特性,允许在持有写锁的情况下获取读锁,然后释放写锁,从而将写锁"降级"为读锁。
public class LockDowngradeDemo {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
private volatile boolean updateAvailable = false;
private Object data;
public void processData() {
readLock.lock();
if (!updateAvailable) {
// 必须释放读锁才能获取写锁
readLock.unlock();
writeLock.lock();
try {
// 重新检查状态,因为可能在此期间被其他线程修改
if (!updateAvailable) {
// 执行数据更新
data = fetchDataFromSource();
updateAvailable = true;
}
// 在释放写锁之前获取读锁 - 锁降级
readLock.lock();
} finally {
writeLock.unlock(); // 写锁释放,但仍然持有读锁
}
}
try {
// 继续使用数据,仍然受到读锁保护
useData(data);
} finally {
readLock.unlock();
}
}
private Object fetchDataFromSource() { /* 数据获取逻辑 */ }
private void useData(Object data) { /* 数据使用逻辑 */ }
}
深入原理:状态设计与锁获取机制
同步状态的设计
ReentrantReadWriteLock使用一个32位的整型变量state来同时管理读锁和写锁的状态:
-
高16位:表示读锁的持有数量
-
低16位:表示写锁的重入次数
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
// 获取读锁数量
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
// 获取写锁数量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
读锁获取逻辑
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
// 如果有写锁被持有,且持有者不是当前线程,获取失败
if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) {
// 成功获取读锁
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
// 更新读锁持有计数
}
return 1;
}
return fullTryAcquireShared(current);
}
写锁获取逻辑
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
// 存在读锁或写锁被其他线程持有
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
if ((w == 0 && writerShouldBlock()) || !compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
写锁优先机制解析
问题的核心:防止写线程饥饿
"写锁请求后,后续的读锁请求会被阻塞" 这一机制的核心目的是防止写线程饥饿问题。
考虑以下场景:
-
系统中持续有读请求到达
-
读锁可以共享,因此读线程可以不断获取锁
-
如果没有写锁优先机制,写线程可能永远无法获得执行机会
实现机制详解
非公平模式下的实现
在非公平模式下,当写锁请求出现时:
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
// 检查等待队列中的第一个节点是否是写锁请求
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null && (s = h.next) != null &&
!s.isShared() && s.thread != null;
}
当写锁在队列中等待时,新来的读锁请求会被阻塞,即使当前没有活跃的写锁。
公平模式下的实现
在公平模式下,规则更加严格:
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
只要队列中有等待的线程(无论是读是写),新来的锁请求都会被加入队列尾部。
场景模拟分析
让我们通过一个时间线来理解这个机制:
时间线: t0 -> t1 -> t2 -> t3 -> t4 -> t5
t0: 读线程R1获取读锁
t1: 读线程R2获取读锁
t2: 写线程W1请求写锁 → 进入等待队列
t3: 读线程R3请求读锁 → 被阻塞(因为W1在等待)
t4: R1释放读锁
t5: R2释放读锁 → W1获得写锁
如果没有这个机制,在t2-t5期间,新的读线程可能会不断获取读锁,导致W1永远无法获得写锁。
性能考量与最佳实践
选择合适的锁模式
// 非公平锁 - 默认选择,吞吐量更高
ReentrantReadWriteLock nonfairLock = new ReentrantReadWriteLock();
// 公平锁 - 保证线程按申请顺序获取锁,避免饥饿但性能较低
ReentrantReadWriteLock fairLock = new ReentrantReadWriteLock(true);
避免死锁的实践
public class SafeReadWriteUsage {
private final ReentrantReadWriteLock lock1 = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock lock2 = new ReentrantReadWriteLock();
// 危险的锁顺序
public void dangerousMethod() {
lock1.readLock().lock();
try {
lock2.writeLock().lock(); // 可能产生死锁
try {
// 业务逻辑
} finally {
lock2.writeLock().unlock();
}
} finally {
lock1.readLock().unlock();
}
}
// 安全的锁顺序 - 统一获取顺序
public void safeMethod() {
lock1.readLock().lock();
lock2.writeLock().lock();
try {
// 业务逻辑
} finally {
lock2.writeLock().unlock();
lock1.readLock().unlock();
}
}
}
性能监控与调试
public class MonitoredReadWriteLock extends ReentrantReadWriteLock {
private final AtomicLong readWaitTime = new AtomicLong();
private final AtomicLong writeWaitTime = new AtomicLong();
@Override
public Lock readLock() {
return new MonitoringReadLock(super.readLock());
}
private class MonitoringReadLock implements Lock {
private final Lock delegate;
MonitoringReadLock(Lock delegate) {
this.delegate = delegate;
}
@Override
public void lock() {
long start = System.nanoTime();
delegate.lock();
long duration = System.nanoTime() - start;
readWaitTime.addAndGet(duration);
}
// 其他方法委托...
}
}
实际应用案例
缓存实现
public class ReadWriteLockCache<K, V> {
private final Map<K, V> cache = new HashMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
public V get(K key) {
readLock.lock();
try {
return cache.get(key);
} finally {
readLock.unlock();
}
}
public void put(K key, V value) {
writeLock.lock();
try {
cache.put(key, value);
} finally {
writeLock.unlock();
}
}
public V computeIfAbsent(K key, Function<K, V> mappingFunction) {
V value = get(key);
if (value == null) {
writeLock.lock();
try {
// 双重检查
value = cache.get(key);
if (value == null) {
value = mappingFunction.apply(key);
cache.put(key, value);
}
} finally {
writeLock.unlock();
}
}
return value;
}
}
总结
ReentrantReadWriteLock通过精巧的状态设计和线程调度策略,在保证数据一致性的同时,显著提升了读多写少场景下的系统吞吐量。写锁优先机制有效防止了写线程饥饿问题,体现了并发设计中公平性与效率的平衡。
在实际应用中,我们需要根据具体的业务场景和性能要求,合理选择锁模式,并遵循最佳实践来避免潜在的死锁和性能问题。理解其内部机制不仅有助于正确使用该工具,更能提升我们对并发编程本质的认识。

图:ReentrantReadWriteLock锁获取基本流程

图:写锁优先机制防止写线程饥饿
往期免费源码对应视频:
免费获取--SpringBoot+Vue宠物商城网站系统
🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐➕关注👀是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝+关注👀欢迎留言讨论
🔥🔥🔥(源码 + 调试运行 + 问题答疑)
🔥🔥🔥 有兴趣可以联系我
💖学习知识需费心,
📕整理归纳更费神。
🎉源码免费人人喜,
🔥码农福利等你领!💖常来我家多看看,
📕网址:扣棣编程,
🎉感谢支持常陪伴,
🔥点赞关注别忘记!💖山高路远坑又深,
📕大军纵横任驰奔,
🎉谁敢横刀立马行?
🔥唯有点赞+关注成!
⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇点击此处获取源码⬇⬇⬇⬇⬇⬇⬇⬇⬇
ReentrantReadWriteLock读写锁深度解析
1232

被折叠的 条评论
为什么被折叠?



