文章目录
一、集成spring-integration-redis
前提项目里面已经正确的集成了spring-boot-starter-data-redis,参考本专栏前面文章《单例、哨兵、集群模式整合》
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-redis</artifactId>
</dependency>
二、注册RedisLockRegistry
完成RedisLockRegistry Bean注册
@Configuration
public class RedisLockConfig {
@Bean
public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
//第一个参数redisConnectionFactory
//第二个参数registryKey,分布式锁前缀,设置为项目名称会好些
//该构造方法对应的分布式锁,默认有效期是60秒.可以自定义
return new RedisLockRegistry(redisConnectionFactory, "boot-launch");
//return new RedisLockRegistry(redisConnectionFactory, "boot-launch",60);
}
}
三、使用RedisLockRegistry
使用RedisLockRegistry完成获取锁、加锁以及完成业务之后的释放锁的操作。
@Resource
private RedisLockRegistry redisLockRegistry;
public void updateUser(String userId) {
String lockKey = "config" + userId;
Lock lock = redisLockRegistry.obtain(lockKey); //获取锁资源
try {
lock.lock(); //加锁
//这里写需要处理业务的业务代码
} finally {
lock.unlock(); //释放锁
}
}
四、RedisLockRegistry解读
org.springframework.integration.redis.util.RedisLockRegistry的核心源码非常简单,就RedisLockRegistry这一个类。源码我就不贴在这里了,我给大家总结一下要点:
- 基于StringRedisTemplate实现,所以与spring-boot-starter-data-redis天然融合。
- RedisLockRegistry可以结合Spring data redis实现分布式锁,registryKey是锁key的前缀。
- 默认的锁过期时间是60秒,提供了自定义RedisLockRegistry(redisConnectionFactory, registryKey,expiredAfter)的构造函数可以使用
- 当尝试去unlock已经过期的锁的时候,会抛出异常IllegalStateException,即RedisLockRegistry不支持锁的续期。
- RedisLockRegistry实现的分布式锁是“可重入”的,可重入就是说某个线程已经获得某个锁,该线程可以再次获取锁而不会出现死锁。基于java.util.concurrent.locks.ReentrantLock实现可重入锁
五、要不要使用注解实现分布式锁?
现在有很多的博文里面给出了一种非常简单的实现,就是在方法上面加注解,比如:
@RedisLock("lock-key")
public void save(){
}
这种实现使用上非常简单,但是笔者不建议使用这种方式,有几个原因
- 不管是什么锁,锁定的范围应该越小越好,琐能保证数据操作多线程安全性,但是会降低应用性能。能对1行代码加锁就完成的需求,就不要锁定2行。把注解加在方法上,是锁定了方法里面所有的代码执行,高并发场景会影响执行效率。(有的同学说可以把需要锁定的代码单独抽取函数,这的确是一个方法,但抽取的粒度过细会破坏代码的可维护性)
- 使用注解的方式其核心原理是使用AOP面向切面编程的实现。异常及事务的处理、分布式锁在我们的应用里面都是面向切面编程的,混合到一起有的时候很难处理。我的建议是分布式锁它并不是一个“常用项”,如果你的项目里面到处都是分布式锁,你要思考一下是不是你的设计出了问题。所以对于非常用项我们没有必要过度封装,我们使用try-finally的方法来使用它就可以了,代码封装少可读性强,如果出现异常处理也都非常灵活,改动锁相关的代码影响面积小。