分布式锁确保在同一时间只有一个节点能获得对共享资源的独占访问权限,从而解决并发访问问题。
Redisson锁(简称看门狗)
它可以实现锁的延长,确保某个线程执行完才能让其他线程进行抢锁操作
引入看门狗机制后
如何使用?
1、引入依赖包
<!--Redisson依赖-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.31.0</version>
</dependency>
2、配置类Config
@Configuration //标明是配置类
public class RedissonConfiguration {
/*注入到IOC容器中*/
@Bean
public RedissonClient redissonClient(){
//1.创建配置对象
Config config = new Config();
// 集群模式
//config.useClusterServers().addNodeAddress("集群ip1", "集群id2");
//2.根据config创建出RedissonClient实例
config.useSingleServer()
.setAddress("redis://192.168.21.131:6379");
//返回 RedissonClient实例
return Redisson.create(config);
}
}
3、编写Controller类
@RestController
@Slf4j
@RequestMapping("/api")
public class LockController {
@Autowired
private RedissonClient redissonClient;
@PutMapping("/watch-dog")
public String redissonLock() {
RLock lock = redissonClient.getLock("anyLock");
try {
//尝试获取锁,tryLock参数分别是:获取锁的最大等待时间(期间重试) ,锁自动释放时间,时间单位
//锁的获取时间(重试获得锁)为5秒,超过时间线程未结束会延长锁的获取时间,其他线程无法获得锁
//boolean isLocked = lock.tryLock(5,10, TimeUnit.SECONDS);
//不指定锁超时时间,锁会无限续期,直到获得锁的业务逻辑失败
boolean isLocked = lock.tryLock(5, TimeUnit.SECONDS);
if (isLocked) {
log.info("获取锁成功"+Thread.currentThread().getName()+":开始睡觉");
Thread.sleep(5*1000);
log.info("获取锁成功"+Thread.currentThread().getName()+":睡5秒,");
// 业务逻辑
Thread.sleep(10*1000);
log.info("获取锁成功"+Thread.currentThread().getName()+":睡眠10秒,超过请求获取锁的时间");
} else {
log.info("锁未释放{}获取锁失败",Thread.currentThread().getName());
return "无法获取锁";
}
} catch (InterruptedException e) {
e.printStackTrace();
return "线程被中断";
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return Thread.currentThread().getName();
}
}
4、代码效果
避雷点
关于finallly释放锁时的踩雷点
直接lock.unlock会导致Redisson抛出异常,这个异常的原因是因为,当多个请求进来时,由于一个线程在占用着锁,其他线程无法获得锁,他就会走到finally中,去释放锁,但是由于它本身不带锁,就会触发异常。
解决办法
在释放之前添加if判断
finally{
if(lock.isHeldByCurrenThread()){
lock.unlock();
}
}
lock.isHeldByCurrenThread()
,用于判断当前锁是否是当`前线程持有的,如果是就释放锁,不是就不让它释放,可以避免线程抛出异常。