定时器+redis分布式锁
在xml中添加:
<task:annotation-driven/>
1. 不带锁的定时器:
2. 带redis分布式锁的定时器:
原理:设置锁的lokkey,值为当前毫秒值+超时毫秒值,redis的setnx方法为,如果不存在lokkey,返回1并设置lokkey,返回0,代表已存在锁
代码
3.上面的方法虽然好,但是如果在设置锁的有效期之前,tomcat异常关闭,比如:杀死tomcat进程,在redis中已经存在锁,这种情况会出现死锁,所以需要进行改进
优化版:设置锁的lokkey,值为当前毫秒值+超时毫秒值,redis的setnx方法为,如果不存在lokkey,返回1并设置lokkey,返回0,代表已存在锁
返回0时,判断当前时间是否为空&&是否 > redis的value值 lockvalueA
{
不为空且大于:使用getset方法,设置值为当前毫秒+超时毫秒,返回旧值 lockvalueB,判断lockvalueB是否为空 || (lockvalueB不为空且lockvalueA==lockvalueB)
{
lockvalueB为空:代表走到这一步,别的tomcat已经执行了一遍代码并且释放了锁
lockvalueB不为空且lockvalueA==lockvalueB:代表开启定时后第一次执行此代码
lockvalueA != lockvalueB:代表别的tomcat已经在执行此代码,并且锁还未被释放
}
不为空且小于:结束
为空:结束
}
代码
@Scheduled(cron="0 */1 * * * ?")
public void closeOrderTaskV3(){
log.info("关闭订单定时任务启动");
long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000"));
Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
if(setnxResult != null && setnxResult.intValue() == 1){
closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}else{
//未获取到锁,继续判断,判断时间戳,看是否可以重置并获取到锁
String lockValueStr = RedisShardedPoolUtil.get(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
if(lockValueStr != null && System.currentTimeMillis() > Long.parseLong(lockValueStr)){
String getSetResult = RedisShardedPoolUtil.getSet(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
//再次用当前时间戳getset。
//返回给定的key的旧值,->旧值判断,是否可以获取锁
//当key没有旧值时,即key不存在时,返回nil ->获取锁
//这里我们set了一个新的value值,获取旧的值。
if(getSetResult == null || (getSetResult != null && StringUtils.equals(lockValueStr,getSetResult))){
//真正获取到锁
closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}else{
log.info("没有获取到分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}
}else{
log.info("没有获取到分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}
}
log.info("关闭订单定时任务结束");
}
定时器+redisson框架分布式锁
使用这个方法代码会简单很多
在pom.xml中添加:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-avro</artifactId>
<version>2.9.0</version>
</dependency>
RedissonManager类
@Component
@Slf4j
public class RedissonManager {
private Config config = new Config();
private Redisson redisson = null;
public Redisson getRedisson() {
return redisson;
}
private static String redis1Ip = PropertiesUtil.getProperty("redis1.ip");
private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));
private static String redis2Ip = PropertiesUtil.getProperty("redis2.ip");
private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));
@PostConstruct
private void init(){
try {
config.useSingleServer().setAddress(new StringBuilder().append(redis1Ip).append(":").append(redis1Port).toString());
redisson = (Redisson) Redisson.create(config);
log.info("初始化Redisson结束");
} catch (Exception e) {
log.error("redisson init error",e);
}
}
}