目录
第13章 redis分布式锁
13.1 分布式锁涉及的命令
13.2 分布式锁流程图
第14章 分布式任务调度
14.1 总览
14.2 基础版代码
14.3 防死锁
14.4 双重防死锁
第13章 redis分布式锁
13.1 分布式锁涉及的命令
setnx:原子性,只有当旧值不存在时才会set成功,返回1,否则返回0。
getset:原子性,设置新值后返回旧值。
expire:设置有效期。
del:删除。
13.2 分布式锁流程图
(1)基本版
(2)改进版
第14章 分布式任务调度
14.1 总览
- redis分布式锁防死锁
- redis分布式锁双重防死锁
14.2 基础版代码
@Component
@Slf4j
public class CloseOrderTask {
@Autowired
private IOrderService iOrderService;
@Scheduled(cron="0 */1 * * * ?")//每1分钟(每个1分钟的整数倍)
public void closeOrderTaskV1(){
log.info("关闭订单定时任务启动");
int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
iOrderService.closeOrder(hour);
log.info("关闭订单定时任务结束");
}
}
14.3 防死锁
为key增加有效期。
缺陷:如果中间中断了,比如锁刚刚放入redis,但是还没来得及设置有效期应用突然被关闭了,或者tomcat被重启了。这个key将会一直存在导致死锁。
@Component
@Slf4j
public class CloseOrderTask {
@Autowired
private IOrderService iOrderService;
@Autowired
private RedissonManager redissonManager;
@Scheduled(cron="0 */1 * * * ?")
public void closeOrderTaskV2(){
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){
//如果返回值是1,代表设置成功,获取锁
closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}else{
log.info("没有获得分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}
log.info("关闭订单定时任务结束");
}
private void closeOrder(String lockName){
RedisShardedPoolUtil.expire(lockName,5);//有效期5秒,防止死锁
log.info("获取{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
iOrderService.closeOrder(hour);
RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
log.info("释放{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
log.info("===============================");
}
}
14.4 双重防死锁
不仅为key增加有效期,还在获取锁的时候判定是否已经超时。如果已经超时且中间没有其他进程修改过,则可以执行任务。
参见上面的流程图2。
@Component
@Slf4j
public class CloseOrderTask {
@Autowired
private IOrderService iOrderService;
@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("关闭订单定时任务结束");
}
private void closeOrder(String lockName){
RedisShardedPoolUtil.expire(lockName,5);//有效期50秒,防止死锁
log.info("获取{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
iOrderService.closeOrder(hour);
RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
log.info("释放{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
log.info("===============================");
}
}