//使用redis作为分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock() {
//TODO 【重要】实际就关心两个问题:1、原子加锁,保证过期时间;2、原子解锁
String uuid = UUID.randomUUID().toString();
//1、占分布式锁,去redis占坑 SETNXEX
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);
if (lock) {
System.out.println("成功获取分布式锁.....");
//加锁成功执行业务
//TODO 抛出问题:若执行业务时出错,没有解锁怎么办?
// 答:加try catch finally解锁。
// 那又有一个问题,执行业务时断电怎么办?
// 答:给锁设置一个过期时间,必须和加锁是同步的,原子的。防止在拿到锁在执行业务前突然断电
Map<String, List<Catelog2Vo>> dataFromDb = null;
try {
dataFromDb = getDataFromDb();
}finally {
//lua脚本
String script = "if redis call('get',KEYS[1]) == ARGV[1] then return redis call('del',KEYS[1]) else return 0 end";
//删锁
stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),/*这里的Long表示返回值的类型,1-删除成功 0-删除不成功*/
Arrays.asList("lock"), uuid);//这里传uuid,是删除对比是不是自己的锁
}
//执行了业务要解锁
//TODO 问题:业务超时,redis早就把锁释放了。现在加锁是其他线程的锁,现在执行删锁岂不是把别人的锁删掉?!
//TODO 答:上锁时的值采用uuid。业务超时的话,设置锁过期时间长一点,300s。业务总不能超过300s吧!!
//String lockValue = stringRedisTemplate.opsForValue().get("lock");
//TODO IO时长问题:在获取lockValue进行IO的时候,返回锁的值给我们的时候,假如返回给我们需要0.5s,在0.3s时锁刚好过期,redis咔咔地帮我们把锁删掉,
// 这个时候又有其他线程进来了加上了锁。那待获取值后再删锁,岂不又删了别人的锁?!
// 答:用lua脚本执行删锁,保证原子性
/*if (lockValue.equals(uuid)) {
//删自己的锁,以uuid作为区分
stringRedisTemplate.delete("lock");
}*/
return dataFromDb;
} else {
//加锁失败,100ms后重试
System.out.println("获取分布式锁失败..正在重试....");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
return getCatalogJsonFromDbWithRedisLock();//自旋
}
}
【笔记】使用redis作为分布式锁,手动版
最新推荐文章于 2022-10-25 19:06:03 发布