在单机应用中,可以使用 Java 内置的锁机制(如 synchronized、ReentrantLock 等)来实现线程间的同步。但在分布式环境下,由于应用部署在多台服务器上,传统的单机锁无法满足需求,这时就需要分布式锁。
分布式锁是一种跨 JVM、跨服务器的锁机制,它能够在分布式系统中对共享资源进行互斥访问控制,确保在同一时间只有一个客户端可以获得锁并执行操作。
Redisson 是一个在 Redis 基础上实现的 Java 驻内存数据网格(In-Memory Data Grid)。它提供了分布式和可扩展的 Java 数据结构,包括分布式锁、分布式集合、分布式对象等功能。
Redisson 的分布式锁基于 Redis 的EVAL
命令(执行 Lua 脚本)实现。它使用了一个 Redis 键值对来表示锁,键是锁的名称,值包含锁的持有者信息和过期时间。
基本流程:
Redisson 提供了多种锁的实现方案:
锁的获取:
-- KEYS[1]是锁的key,ARGV[1]是线程标识,ARGV[2]是过期时间
if (redis.call('exists', KEYS[1]) == 0) or (redis.call('hexists', KEYS[1], ARGV[1]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[1], 1);
redis.call('pexpire', KEYS[1], ARGV[2]);
return nil;
end;
return redis.call('pttl', KEYS[1]);
锁的释放:
-- KEYS[1]是锁的key,ARGV[1]是线程标识
if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then
return nil;
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1);
if (counter > 0) then
return 0;
else
redis.call('del', KEYS[1]);
return 1;
end;
org.redisson
redisson
3.23.3
@Configuration
public class RedissonConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
@Value("${spring.data.redis.password}")
private String password;
@Value("${spring.data.redis.database}")
private int database;
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://" + host + ":" + port)
.setDatabase(database);
if (password != null && !password.isEmpty()) {
config.useSingleServer().setPassword(password);
}
return Redisson.create(config);
}
}
@Service
public class LockService {
@Resource
private RedissonClient redissonClient;
public void doSomething() {
RLock lock = redissonClient.getLock("myLock");
try {
// 尝试获取锁,最多等待100秒,锁有效期为30秒
boolean isLocked = lock.tryLock(100, 30, TimeUnit.SECONDS);
if (isLocked) {
// 业务处理
System.out.println("执行业务逻辑");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 释放锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
RLock lock = redissonClient.getLock("myLock");
lock.lock();
try {
try {
lock.lock();
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
RLock fairLock = redissonClient.getFairLock("myFairLock");
fairLock.lock();
try {
// 业务处理
} finally {
fairLock.unlock();
}
RReadWriteLock rwLock = redissonClient.getReadWriteLock("myRWLock");
// 读锁(共享)
RLock readLock = rwLock.readLock();
readLock.lock();
try {
// 读取操作
} finally {
readLock.unlock();
}
// 写锁(排他)
RLock writeLock = rwLock.writeLock();
writeLock.lock();
try {
// 写入操作
} finally {
writeLock.unlock();
}
RLock lock1 = redissonClient.getLock("lock1");
RLock lock2 = redissonClient.getLock("lock2");
RLock lock3 = redissonClient.getLock("lock3");
// 组合多个锁
RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2, lock3);
multiLock.lock();
try {
// 业务处理
} finally {
multiLock.unlock();
}
看门狗(Watchdog)机制是 Redisson 为分布式锁提供的一种自动续期功能。它能够在客户端持有锁期间,自动延长锁的有效期,防止因为执行时间过长导致锁过期被其他客户端获取,从而破坏互斥性。
lock()
方法获取锁时(不设置过期时间),Redisson 会默认设置一个 30 秒的锁有效期Redisson 中看门狗的核心实现在RedissonLock.java
类中:
// 锁的自动续期逻辑
private void scheduleExpirationRenewal(long threadId) {
ExpirationEntry entry = new ExpirationEntry();
ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);
if (oldEntry != null) {
oldEntry.addThreadId(threadId);
} else {
entry.addThreadId(threadId);
renewExpiration();
}
}
// 续期定时任务
private void renewExpiration() {
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
// 续期逻辑
// ...
// 每internalLockLeaseTime/3时间后,重新检查并续期
commandExecutor.getConnectionManager().newTimeout(this,
internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}
启用看门狗机制(默认):
// 不指定过期时间,默认启用看门狗机制
lock.lock();
禁用看门狗机制:
// 明确指定过期时间,看门狗机制将被禁用
lock.lock(10, 30, TimeUnit.SECONDS);
可以通过配置修改看门狗的默认行为:
// 设置锁的默认过期时间(看门狗续期间隔为该值的1/3)
Config config = new Config();
config.setLockWatchdogTimeout(30000); // 30秒
RedissonClient redisson = Redisson.create(config);
try {
// 业务逻辑
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
int retryCount = 3;
while (retryCount > 0) {
boolean locked = lock.tryLock(5, TimeUnit.SECONDS);
if (locked) {
try {
// 业务逻辑
return result;
} finally {
lock.unlock();
}
}
retryCount--;
Thread.sleep(1000);
}
// 降级处理
return fallbackMethod();
Redis 原生命令(SETNX + EXPIRE):
Redisson:
Zookeeper:
Redisson:
问题:一个客户端释放了其他客户端持有的锁
解决方案:
isHeldByCurrentThread()
方法检查问题:业务执行时间超过锁的有效期
解决方案:
问题:Redis 服务器故障或重启
解决方案:
参与评论
手机查看
返回顶部