分布式锁:Redisson还实现了Redis文档中提到像分布式锁Lock这样的更高阶应用场景。事实上Redisson并没有不止步于此,在分布式锁的基础上还提供了联锁(MultiLock),读写锁(ReadWriteLock),公平锁(Fair Lock),红锁(RedLock),信号量(Semaphore),可过期性信号量(PermitExpirableSemaphore)和闭锁(CountDownLatch)这些实际当中对多线程高并发应用至关重要的基本部件。正是通过实现基于Redis的高阶应用方案,使Redisson成为构建分布式系统的重要工具。
依赖
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.15.5</version>
</dependency>
样例
@Autowired
RedissonClient redissonClient;
public String TestLock() {
// 1.获取锁,只要锁的名字一样,获取到的锁就是同一把锁。
RLock lock = redisson.getLock("redisson-lock");
// 2.加锁
lock.lock();
try {
System.out.println("加锁成功,执行后续代码。线程 ID:" + Thread.currentThread().getId());
Thread.sleep(10000);
} catch (Exception e) {
//TODO
} finally {
lock.unlock();
// 3.解锁
System.out.println("Finally,释放锁成功。线程 ID:" + Thread.currentThread().getId());
}
return "test lock ok";
}
锁无过期时间情况:服务停了,锁会释放-看门狗原理
如果负责储存这个分布式锁的 Redisson 节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。
默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。
如果我们未制定 lock 的超时时间,就使用 30 秒作为看门狗的默认时间。只要占锁成功,就会启动一个定时任务:每隔 10 秒重新给锁设置过期的时间,过期时间为 30 秒。
设置锁过期时间
RLock lock = redissonClient.getLock("lock-expireTime");
lock.lock(10000, java.util.concurrent.TimeUnit.MILLISECONDS);
不使用此框架的情况下
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class RedisTemplateDistributedLock {
private RedisTemplate<String, Object> redisTemplate;
public RedisTemplateDistributedLock(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
// 设置序列化器,避免乱码问题
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
}
/**
* 获取分布式锁
* @param lockKey 锁的键
* @param expireTime 锁的过期时间
* @param timeUnit 时间单位
* @return 锁的唯一标识,如果获取失败返回 null
*/
public String tryLock(String lockKey, long expireTime, TimeUnit timeUnit) {
String requestId = UUID.randomUUID().toString();
Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, timeUnit);
return result != null && result ? requestId : null;
}
/**
* 释放分布式锁
* @param lockKey 锁的键
* @param requestId 锁的唯一标识
* @return 是否成功释放锁
*/
public boolean releaseLock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Long result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId);
return result != null && result == 1;
}
}
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.concurrent.TimeUnit;
public class RedisTemplateDistributedLockUsage {
public static void main(String[] args) {
// 假设这里已经有 RedisConnectionFactory 的实例
RedisConnectionFactory redisConnectionFactory = null;
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
RedisTemplateDistributedLock lock = new RedisTemplateDistributedLock(redisTemplate);
String lockKey = "myDistributedLock";
long expireTime = 10;
TimeUnit timeUnit = TimeUnit.SECONDS;
// 尝试获取锁
String requestId = lock.tryLock(lockKey, expireTime, timeUnit);
if (requestId != null) {
try {
System.out.println("成功获取到锁,开始执行临界区代码");
// 模拟业务操作
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
// 释放锁
boolean released = lock.releaseLock(lockKey, requestId);
if (released) {
System.out.println("锁已成功释放");
} else {
System.out.println("锁释放失败");
}
}
} else {
System.out.println("未能获取到锁");
}
}
}