项目开发中我们经常需要用到分布式锁,尤其是在集群环境多台服务器情况下,比如 接口请求、定时任务等业务环境中;
今天 我们先介绍一种 redis 分布式锁,在普通项目中就可以使用,代码如下:
1、redis 加锁和释放锁代码如下:
package com.jy.utils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCommands;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* RedisTemplate工具类 通过注解(@Component)扫描此类
*/
@Component
public class RedisUtil implements ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);
private static RedisTemplate<Object, Object> redisTemplate;//redis 模板类
private static ThreadLocal<String> lockFlag = new ThreadLocal<String>();
public static final String UNLOCK_LUA;
//redis 加锁方法
public static Boolean setNX(String key,long timeout) {
String value = String.valueOf(System.currentTimeMillis());//锁的value 值设置
Boolean isExit = false;
try {
//设置锁的核心代码
isExit = redisTemplate.getConnectionFactory().getConnection().setNX(key.getBytes(), value.getBytes());
/** 如果设置成功,要设置其过期时间 */
if (isExit) {
redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
logger.info(key+":成功获取到锁");
return isExit;
}
}catch (Exception e){
logger.info(key+":获取锁失败",e);
}
logger.info(key+":未获取到锁");
return isExit;
}
//释放锁的核心方法
public static void releaseLock(String key ){
try {
redisTemplate.opsForValue().getOperations().delete(key);//直接删除key
logger.info(key+";锁释放成功");
} catch (Exception ex) {
logger.error(key+";锁释放失败",ex);
}
}
//通过springIOC容器取redis模板类
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
redisTemplate = applicationContext.getBean(RedisTemplate.class);
}
}
2、redis 分布式锁就这么简单,但是!此种方案是有有坑的,在普通项目、并发量小、定时任务频率低、服务稳定的的情况下勉强可以使用。分析一下原因:首先,该加锁过程不能保证原子性,比如刚刚加锁之后,还没有来得及设置超时时间,服务出问题了,或者中间出现了异常代码,那么这个锁没有设置超时时间,就可能成为死锁(如果业务代码中没有成功释放锁)。
其次:关于释放锁,如果在多线程情况下,可能会把其他线程的锁(同一个key : 锁)释放掉,这对业务来说是非常危险的。
下篇我将介绍另一种redis 分布式锁,能很好的弥补这种方案的漏洞,敬请期待!下篇地址:
版权声明:本文为nandao158原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。