概述:
RReadWriteLock
是一个在分布式环境中使用的读写锁,它通常是由分布式缓存或数据存储系统(如Redisson)提供的。这个锁允许多个读操作同时进行,而写操作则是互斥的。
原理:
- 读写锁分离: 读写锁允许多个读操作同时进行,只要没有写操作。当写锁被持有时,其他的读写操作都会被阻塞。
- 锁降级: 写锁持有者可以降级为读锁,这是通过首先获取读锁,然后释放写锁来完成的。这允许锁的持有者继续以读取者的身份持有锁。
- 锁升级: 通常,读锁不能直接升级为写锁,因为这可能会导致死锁。如果需要写入,读锁持有者必须释放读锁并独立获取写锁。
- 分布式环境 : 在分布式系统中,
RReadWriteLock
通常使用分布式存储(如Redis)来保持锁的状态。 锁的状态需要跨多个节点同步,以确保锁的一致性和正确性。 - 公平性: 某些实现支持公平锁,这意味着锁的分配是按照请求的顺序来的,以避免饥饿问题。
优点:
- 提高并发性: 读写锁允许多个读操作并发执行,这在读多写少的场景中可以显著提高性能。
- 资源共享: 读锁的共享性质允许更多的线程同时访问共享资源。
- 分布式支持 :
RReadWriteLock
适用于分布式系统,可以在多个节点间同步锁的状态。 - 死锁减少: 相比于传统的互斥锁,读写锁在某些情况下可以减少死锁的可能性。
缺点:
- 复杂性: 读写锁的管理比简单的互斥锁更复杂,需要正确处理读和写锁的交互。
- 性能开销: 分布式锁需要网络通信来同步状态,这可能增加延迟和性能开销。
- 写锁饥饿: 在高读负载的情况下,写锁可能会遭受饥饿,因为总是有新的读锁被获取。
- 设计考虑: 在设计分布式系统时,必须考虑锁的粒度和持有时间,以避免性能瓶颈。
- 一致性挑战: 在分布式环境中保持锁状态的一致性可能是一个挑战,尤其是在网络分区或节点故障的情况下。
使用场景:
-
缓存系统 :在读多写少的缓存系统中,
RReadWriteLock
可以用于保护缓存的一致性。读操作可以并发执行,而写操作(更新缓存)则需要独占锁。 -
系统配置 :当系统配置需要被多个服务实例共享并可能被更新时,
RReadWriteLock
可以用来保证配置更新的原子性和一致性。 -
数据库访问 :在执行复杂的事务或批量操作时,
RReadWriteLock
可以用来防止其他操作干扰这些操作。 -
分布式计算 :在分布式计算任务中,
RReadWriteLock
可以用来同步不同计算节点间的状态
RReadWriteLock流程图:
基于 RReadWriteLock
的锁获取和释放流程:
这个流程图中:
- 开始节点 "Start" 表示流程的开始。
- "Get RReadWriteLock" 是一个决策节点,用于获取读锁或写锁。
- "RLock readLock = readWriteLock.readLock()" 和 "RLock writeLock = readWriteLock.writeLock()" 表示获取读锁或写锁的操作。
- "Try to Acquire Read Lock" 和 "Try to Acquire Write Lock" 是尝试获取锁的决策节点。
- "Perform Read Operations" 和 "Perform Write Operations" 表示在锁保护下执行的操作。
- "Release Read Lock" 和 "Release Write Lock" 表示释放锁的操作。
- "Handle Read Lock Failure" 和 "Handle Write Lock Failure" 表示处理获取锁失败的情况。
- "End" 表示流程的结束。
RReadWriteLock时序图:
基于 RReadWriteLock
的锁获取和释放的交互过程:
这个时序图中:
- "Client" 是发起锁请求的客户端或者代码。
- "RReadWriteLock" 是 Redisson 提供的读写锁的抽象。
- "ReadLock" 和 "WriteLock" 分别代表读锁和写锁。
时序图展示了以下步骤:
- 客户端请求从
RReadWriteLock
获取读写锁(getReadWriteLock(lockKey)
)。 - 根据需要获取读锁或写锁的路径,时序图分为两个分支:
- 在获取读锁的分支中,
RReadWriteLock
提供了读锁对象(readLock()
),客户端尝试获取读锁(tryLock(timeout, timeUnit)
),如果成功,执行读操作,然后释放锁(unlock()
)。 - 在获取写锁的分支中,
RReadWriteLock
提供了写锁对象(writeLock()
),客户端尝试获取写锁,如果成功,执行写操作,然后释放锁。
- 在获取读锁的分支中,
RReadWriteLock 类图:
这个类图中:
RLock
是一个接口或抽象类,提供了锁的基本操作,如lock()
、unlock()
和tryLock()
。RReadWriteLock
是一个接口,提供了获取读锁和写锁的方法,这两个方法返回RLock
类型的对象。RedissonClient
是一个类,它提供了获取RReadWriteLock
实例的方法getReadWriteLock()
。
关系说明:
RReadWriteLock
继承自RLock
(在这个示例中,这是一个简化的假设,实际上RReadWriteLock
可能不会直接继承RLock
,但会提供访问读锁和写锁的方法,这两种锁都实现了RLock
接口)。RedissonClient
创建RReadWriteLock
实例的关系用虚线箭头表示。
ReadWriteLock 工具类
DistributedReadWriteLockHelper
基于RReadWriteLock 这边封装了一个功能较为完整的工具类:
java
import lombok.Data;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* @Author derek_smart
* @Date 202/5/10 8:41
* @Description ReadWriteLock 工具类
*/
@Component
public class DistributedReadWriteLockHelper {
@Autowired
private RedissonClient redissonClient;
public LockHandler acquireReadLock(String lockKey) {
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(lockKey);
RLock readLock = readWriteLock.readLock();
readLock.lock();
return new LockHandler(readLock);
}
public LockHandler acquireWriteLock(String lockKey) {
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(lockKey);
RLock writeLock = readWriteLock.writeLock();
writeLock.lock();
return new LockHandler(writeLock);
}
public LockHandler tryAcquireReadLock(String lockKey, long timeout, TimeUnit timeUnit) throws InterruptedException {
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(lockKey);
RLock readLock = readWriteLock.readLock();
if (readLock.tryLock(timeout, timeUnit)) {
return new LockHandler(readLock);
}
return null;
}
public LockHandler tryAcquireWriteLock(String lockKey, long timeout, TimeUnit timeUnit) throws InterruptedException {
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(lockKey);
RLock writeLock = readWriteLock.writeLock();
if (writeLock.tryLock(timeout, timeUnit)) {
return new LockHandler(writeLock);
}
return null;
}
public LockHandler tryAcquireWriteLockWithFallback(String lockKey, long timeout, TimeUnit timeUnit, Supplier<LockHandler> fallbackSupplier) {
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(lockKey);
RLock writeLock = readWriteLock.writeLock();
try {
if (writeLock.tryLock(timeout, timeUnit)) {
return new LockHandler(writeLock);
} else {
return fallbackSupplier.get();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Thread interrupted while trying to acquire write lock", e);
}
}
/**
* 尝试在指定的超时时间内获取写锁,并且锁具有租约时间,超过租约时间后锁会自动释放。这可以防止在节点故障时发生死锁。
* 同时,`LockHandler` 类中添加了 `renewLock` 方法,用于在锁快到期时续约锁。
*
* @param lockKey
* @param acquireTimeout
* @param lockLeaseTime
* @param timeUnit
* @return
*/
public LockHandler tryAcquireWriteLockWithTimeout(String lockKey, long acquireTimeout, long lockLeaseTime, TimeUnit timeUnit) {
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(lockKey);
RLock writeLock = readWriteLock.writeLock();
try {
if (writeLock.tryLock(acquireTimeout, lockLeaseTime, timeUnit)) {
return new LockHandler(writeLock);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Thread interrupted while trying to acquire write lock", e);
}
return null;
}
// Method to try to acquire a lock with retry logic
public LockHandler tryAcquireLockWithRetry(String lockKey, boolean writeLock, LockConfig lockConfig, LockCallback callback) {
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(lockKey);
RLock lock = writeLock ? readWriteLock.writeLock() : readWriteLock.readLock();
int attempts = lockConfig.getDefaultRetryAttempts();
long interval = lockConfig.getDefaultRetryInterval();
while (attempts > 0) {
try {
if (lock.tryLock(lockConfig.getDefaultAcquireTimeout(), lockConfig.getDefaultLeaseTime(), lockConfig.getDefaultTimeUnit())) {
if (callback != null) {
callback.onLockAcquired();
}
return new LockHandlerN(lock, lockConfig);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
attempts--;
try {
TimeUnit.SECONDS.sleep(interval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
return null;
}
public static class LockHandlerN extends LockHandler implements AutoCloseable {
private final RLock lock;
private final LockConfig lockConfig;
private LockHandlerN(RLock lock, LockConfig lockConfig) {
super();
this.lock = lock;
this.lockConfig = lockConfig;
}
@Override
public void close() {
try {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
// Log unlock event
}
} catch (Exception e) {
// Log exception
}
}
public void renewLockIfNecessary() {
try {
if (lock.isHeldByCurrentThread() && !lock.isLocked()) {
lock.lock(lockConfig.getDefaultLeaseTime(), lockConfig.getDefaultTimeUnit());
// Log renew event
}
} catch (Exception e) {
// Log exception
}
}
}
public static class LockHandler implements AutoCloseable {
private RLock lock;
private LockHandler(RLock lock) {
this.lock = lock;
}
public LockHandler() {
}
@Override
public void close() {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
public boolean isLocked() {
return lock.isLocked();
}
// 可以添加方法支持锁的升级和降级
public void upgrade() {
// 实现从读锁升级到写锁的逻辑
}
// 实现从写锁降级到读锁的逻辑
public void downgrade(RReadWriteLock readWriteLock) {
if (!isHeldByCurrentThread()) {
throw new IllegalStateException("Cannot downgrade a lock not held by current thread");
}
RLock readLock = readWriteLock.readLock();
readLock.lock();
lock.unlock();
}
public boolean isHeldByCurrentThread() {
return lock.isHeldByCurrentThread();
}
// 可以添加续约方法,如果需要的话
public void renewLock(long leaseTime, TimeUnit timeUnit) {
if (isHeldByCurrentThread()) {
lock.lock(leaseTime, timeUnit);
}
}
}
@Data
public class LockConfig {
private long defaultAcquireTimeout = 30;
private long defaultLeaseTime = 60;
private TimeUnit defaultTimeUnit = TimeUnit.SECONDS;
private int defaultRetryAttempts = 3;
private long defaultRetryInterval = 5;
}
/* public interface LockCallback {
void onLockAcquired();
}*/
}
真正使用时,请把LockCallback LockConfig 单独封装一个类,这边是为了实现一个类显示所有。
DistributedReadWriteLockTestService测试类:
java
import org.springframework.beans.factory.annotation.Autowired;
import java.util.concurrent.TimeUnit;
/**
* @Author derek_smart
* @Date 202/5/10 9:41
* @Description ReadWriteLock 测试类
*/
public class DistributedReadWriteLockTestService {
@Autowired
private DistributedReadWriteLockHelper lockHelper;
public void performReadOperation(String lockKey) {
// 获取读锁
DistributedReadWriteLockHelper.LockHandler readLockHandler = lockHelper.acquireReadLock(lockKey);
try {
// 执行需要读锁保护的操作
} finally {
// 释放读锁
readLockHandler.close();
}
}
public void performWriteOperation(String lockKey) {
// 获取写锁
DistributedReadWriteLockHelper.LockHandler writeLockHandler = lockHelper.acquireWriteLock(lockKey);
try {
// 执行需要写锁保护的操作
} finally {
// 释放写锁
writeLockHandler.close();
}
}
public void performReadOperationWithTimeout(String lockKey, long timeout, TimeUnit timeUnit) {
// 尝试获取读锁,带有超时
try {
DistributedReadWriteLockHelper.LockHandler readLockHandler = lockHelper.tryAcquireReadLock(lockKey, timeout, timeUnit);
if (readLockHandler != null) {
try {
// 执行需要读锁保护的操作
} finally {
// 释放读锁
readLockHandler.close();
}
} else {
// 未能获取读锁
}
} catch (InterruptedException e) {
// 处理中断异常
Thread.currentThread().interrupt();
}
}
public void performWriteOperationWithTimeout(String lockKey, long timeout, TimeUnit timeUnit) {
// 尝试获取写锁,带有超时
try {
DistributedReadWriteLockHelper.LockHandler writeLockHandler = lockHelper.tryAcquireWriteLock(lockKey, timeout, timeUnit);
if (writeLockHandler != null) {
try {
// 执行需要写锁保护的操作
} finally {
// 释放写锁
writeLockHandler.close();
}
} else {
// 未能获取写锁
}
} catch (InterruptedException e) {
// 处理中断异常
Thread.currentThread().interrupt();
}
}
public void performWriteOperationWithFallback(String lockKey, long timeout, TimeUnit timeUnit) {
// 尝试获取写锁,如果失败则使用回退策略
DistributedReadWriteLockHelper.LockHandler writeLockHandler = lockHelper.tryAcquireWriteLockWithFallback(lockKey, timeout, timeUnit, () -> {
// 提供回退策略的锁处理器
return null; // 或者实现一个备用的 LockHandler
});
if (writeLockHandler != null) {
try {
// 执行需要写锁保护的操作
} finally {
// 释放写锁
writeLockHandler.close();
}
} else {
// 回退策略被触发,未能获取写锁
}
}
}
DistributedReadWriteLockHelper总结
- 基本功能:提供了获取读锁和写锁的基本方法。
- 超时处理:支持尝试获取锁时带有超时设置。
- 回退策略:允许在无法获取写锁时提供一个回退策略。
- 重试逻辑:提供了在获取锁失败时进行重试的方法。
- 锁处理器 :通过
LockHandler
类管理锁的释放,实现了AutoCloseable
接口,方便在 try-with-resources 语句中自动释放锁。
进一步优化的建议
以上实现的DistributedReadWriteLockHelper 有一定可取性和一定弊端,大家再使用时根据自己情况进一步的优化,以下就是因为时间原因没有实现自身
- 异常处理 :
- 自定义异常类,代替
RuntimeException
,以提供更多关于锁获取失败的上下文信息。
- 自定义异常类,代替
- 配置灵活性 :
- 允许更灵活的配置选项,例如通过外部配置文件设置默认的超时时间、重试次数等。
- 锁的状态监控 :
- 实现一个监控系统,记录锁的获取和释放事件,以及失败次数和原因,有助于故障排查和系统监控。在必要时续约,以避免在长时间操作中锁过期。
- 锁的升级和降级 :
- 提供安全的锁升级(从读锁到写锁)和降级(从写锁到读锁)机制,确保不会导致死锁。
- 性能优化 :
- 分析锁操作的性能,并根据实际使用情况进行优化,以减少锁的等待时间和提高系统吞吐量。
- 可观测性和日志记录 :
- 增强日志记录,记录关键操作和异常情况,以及提供可观测性工具,如指标和仪表板,以便于系统的监控和警报。
- 分布式锁的最佳实践 :
- 确保遵循分布式锁的最佳实践,例如避免长时间持有锁,最小化锁范围,以及在合适的时机释放锁。
- 锁的公平性 :
- 如果业务场景需要,可以提供公平锁的配置选项,以确保线程按照请求锁的顺序获得锁。
通过实施这些建议,可以提高 DistributedReadWriteLockHelper
的可用性、可靠性和性能,从而更好地服务于需要分布式锁功能的应用程序。
总结:
RReadWriteLock
是 Redisson 提供的一个接口,模拟了Java并发包中的 java.util.concurrent.locks.ReadWriteLock
接口,但是它是为分布式环境设计的。DistributedReadWriteLockHelper
是一个围绕 RReadWriteLock
提供的辅助工具,旨在简化分布式锁的管理,而 RReadWriteLock
是Redisson实现的一个接口,提供分布式读写锁的核心功能。