1.原理解释
Redis分布式锁的实现网上有很多种写法,原理基本就是使用redis自带的命令setnx来实现.此命令特性:
将 key 的值设为 value ,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作
SETNX 是『SET if Not Exists』(如果不存在,则 SET)的简写
设置成功,返回 1 。
设置失败,返回 0 。
2.实现方法
2.1 使用代理模式,在方法执行前和执行后可以添加其他处理程序,本文采用springAOP+java自定义注解方式。
2.2 集成redis,封装redis工具类,包含对redis setnx方法的封装
2.3 本文也支持过期时间设置(防止死锁),默认30s
Ps:本文一部分代码和之前发的的博客相似 ,可以参考 手写redis@Cacheable注解 支持过期时间设置
只是在Redis工具类中增加对redis setnx方法的封装,也是从网上找的轮子><
redis官方推荐(今天偶然看到分享):https://github.com/redisson/redisson/wiki/8.-distributed-locks-and-synchronizers
3.源代码
本文为springboot环境2.0
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
redis配置类
JedisConfig
package com.huajie.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties
public class JedisConfig {
@Value("${spring.redis.host}")
public String host;
@Value("${spring.redis.port}")
public int port;
@Value("${spring.redis.password}")
public String password;
@Value("${spring.redis.database}")
public int database;
@Value("${spring.redis.jedis.pool.max-idle}")
public int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
public int minIdle;
@Value("${spring.redis.jedis.pool.max-active}")
public int maxActive;
@Value("${spring.redis.jedis.pool.max-wait}")
public String maxWait;
@Value("${spring.redis.timeout}")
public String timeout;
}
RedisConfig
package com.huajie.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
/**
* Redis缓存配置类
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Autowired
JedisConfig jedisConfig;
@Autowired
JedisConnectionFactory jedisConnectionFactory;
@Bean
public JedisConnectionFactory jedisConnectionFactory (){
RedisStandaloneConfiguration rf=new RedisStandaloneConfiguration();
rf.setDatabase(jedisConfig.database);
rf.setHostName(jedisConfig.host);
rf.setPort(jedisConfig.port);
rf.setPassword(RedisPassword.of(jedisConfig.password));
int to=Integer.parseInt(jedisConfig.timeout.substring(0,jedisConfig.timeout.length()-2));
//JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();
//jedisClientConfiguration.connectTimeout(Duration.ofMillis(to));
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpb=
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder();
JedisPoolConfig jedisPoolConfig=new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(jedisConfig.maxIdle);
jedisPoolConfig.setMinIdle(jedisConfig.minIdle);
jedisPoolConfig.setMaxTotal(jedisConfig.maxActive);
int l=Integer.parseInt(jedisConfig.maxWait.substring(0,jedisConfig.maxWait.length()-2));
jedisPoolConfig.setMaxWaitMillis(l);
jpb.poolConfig(jedisPoolConfig);
JedisConnectionFactory jedisConnectionFactory=new JedisConnectionFactory(rf,jpb.build());
return jedisConnectionFactory;
}
@Bean
public RedisTemplate redisTemplate(){
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(jedisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
application.yml中redis配置
redis:
database: 0
timeout: 3000ms
host: 192.168.1.87
port: 6379
password: huajie
# 连接池最大连接数(使用负值表示没有限制)
jedis:
pool:
max-idle: 500
min-idle: 50
max-active: 2000
max-wait: 1000ms
# cluster:
# max-redirects: 10
# nodes: 127.0.0.1:6080
RedisLock 注解类,key为加锁的key值,及setnx的值,expireTime为过期时间
package com.huajie.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
String key() default "redisLockKey";
int expireTime() default 30;//30秒
}
RedisLockAspect AOP处理类,对该类方法(有@RrdisLock注解),进行加锁的处理,处理完成后释放锁
@Order(9) 主要是和之前 手写@Cacheable的AOP处理类进行先后执行顺序 @Cacheable为Order(10)
package com.huajie.aspect;
import com.huajie.annotation.RedisLock;
import com.huajie.utils.RedisUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* redis分布式锁处理 不适用与内部方法调用(this.)或者private
*/
@Component
@Aspect
@Order(9)
public class RedisLockAspect {
@Autowired
private RedisUtil redisUtil;
@Pointcut("@annotation(com.huajie.annotation.RedisLock)")
public void annotationPointcut() {
}
@Around("annotationPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 获得当前访问的class
Class<?> className = joinPoint.getTarget().getClass();
// 获得访问的方法名
String methodName = joinPoint.getSignature().getName();
// 得到方法的参数的类型
Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
Object[] args = joinPoint.getArgs();
String key = "";
int expireTime = 30;//秒
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
method.setAccessible(true);
// 判断是否存在@RedisLock注解
if (method.isAnnotationPresent(RedisLock.class)) {
RedisLock annotation = method.getAnnotation(RedisLock.class);
key = getRedisKey(args, annotation.key());
expireTime = getExpireTime(annotation);
}
} catch (Exception e) {
throw new RuntimeException("redis分布式锁注解参数异常", e);
}
Object res = new Object();
if (redisUtil.lock(key, expireTime, TimeUnit.SECONDS)) {
try {
res = joinPoint.proceed();
return res;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
redisUtil.unlock(key);
}
}
return res;
}
private int getExpireTime(RedisLock annotation) {
return annotation.expireTime();
}
private String getRedisKey(Object[] args, String primalKey) {
// 获取#p0...集合
List<String> keyList = getKeyParsList(primalKey);
for (String keyName : keyList) {
int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", ""));
Object parValue = args[keyIndex];
primalKey = primalKey.replace(keyName, String.valueOf(parValue));
}
return primalKey.replace("+", "").replace("'", "");
}
// 获取key中#p0中的参数名称
private static List<String> getKeyParsList(String key) {
List<String> ListPar = new ArrayList<String>();
if (key.indexOf("#") >= 0) {
int plusIndex = key.substring(key.indexOf("#")).indexOf("+");
int indexNext = 0;
String parName = "";
int indexPre = key.indexOf("#");
if (plusIndex > 0) {
indexNext = key.indexOf("#") + key.substring(key.indexOf("#")).indexOf("+");
parName = key.substring(indexPre, indexNext);
} else {
parName = key.substring(indexPre);
}
ListPar.add(parName.trim());
key = key.substring(indexNext + 1);
if (key.indexOf("#") >= 0) {
ListPar.addAll(getKeyParsList(key));
}
}
return ListPar;
}
}
redis工具类封装
package com.huajie.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Redis工具类
*
* @author ZENG.XIAO.YAN
* @date 2018年6月7日
*/
@Component
public final class RedisUtil {
private final long DEFAULT_TIMEOUT = 3600;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String TYPE_NAME = RedisUtil.class.getTypeName();
/**
* 默认30ms尝试一次
*/
private final static long LOCK_TRY_INTERVAL = 30L;
/**
* 默认尝试20s
*/
private final static long LOCK_TRY_TIMEOUT = 20 * 1000L;
/**
* 单个业务持有锁的时间30s,防止死锁
*/
private final static long LOCK_EXPIRE = 30 * 1000L;
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
redisTemplate.expire(key, DEFAULT_TIMEOUT, TimeUnit.SECONDS);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取锁
*
* @param key 锁Key
* @return 是否获取锁
*/
public boolean lock(String key) {
return getLock(key, 0, LOCK_EXPIRE, TimeUnit.MILLISECONDS);
}
/**
* 获取锁
*
* @param key 锁Key
* @param expire 有效期
* @param expireUnit 有效期时间单位
* @return 是否获取锁
*/
public boolean lock(String key, long expire, TimeUnit expireUnit) {
return getLock(key, 0, expire, expireUnit);
}
/**
* 尝试获取锁
*
* @param key 锁Key
* @return 是否获取锁
*/
public boolean tryLock(String key) {
return tryLock(key, LOCK_TRY_TIMEOUT, TimeUnit.MILLISECONDS);
}
/**
* 尝试获取锁
*
* @param key 锁Key
* @param timeout 等待超时时间
* @param unit 等待超时时间单位
* @return 是否获取锁
*/
public boolean tryLock(String key, long timeout, TimeUnit unit) {
// 超时时间转成毫秒
timeout = TimeUnit.MILLISECONDS.convert(timeout, unit);
return getLock(key, timeout, LOCK_EXPIRE, TimeUnit.MILLISECONDS);
}
/**
* 尝试获取锁
*
* @param key 锁Key
* @param timeout 等待超时时间
* @param timeoutUnit 等待超时时间单位
* @param expire 有效期
* @param expireUnit 有效期时间单位
* @return
*/
public boolean tryLock(String key, long timeout, TimeUnit timeoutUnit, long expire, TimeUnit expireUnit) {
// 超时时间转成毫秒
timeout = TimeUnit.MILLISECONDS.convert(timeout, timeoutUnit);
return getLock(key, timeout, expire, expireUnit);
}
/**
* 释放锁
*
* @param key 锁Key
*/
public void unlock(String key) {
key = getPrefix(TYPE_NAME) + key;
Long oldExpireTime = (Long) redisTemplate.opsForValue().get(key);
if (null != oldExpireTime && oldExpireTime >= System.currentTimeMillis()) {
// 大于过期时间,则删除key
redisTemplate.delete(key);
}
}
/**
* 获取锁
*
* @param key 锁键值
* @param timeout 超时时间
* @param time 全局锁生命周期
* @param unit 时间单位
* @return 是否获取到锁
*/
private boolean getLock(String key, long timeout, long time, TimeUnit unit) {
key = getPrefix(TYPE_NAME) + key;
try {
long startTimeMillis = System.currentTimeMillis();
do {
long newValue = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(time, unit);
Boolean isOk = redisTemplate.opsForValue().setIfAbsent(key, newValue);
if (isOk) {
// 获得锁
redisTemplate.expire(key, time, unit);
return true;
}
// 获取过期时间
Long oldExpireTime = (Long) redisTemplate.opsForValue().get(key);
if (null == oldExpireTime) {
oldExpireTime = 0L;
}
if (oldExpireTime >= System.currentTimeMillis()) {
// 不小于系统时间并且过了超时时间,则不获取锁
if ((System.currentTimeMillis() - startTimeMillis) > timeout) {
return false;
}
// 休眠
Thread.sleep(LOCK_TRY_INTERVAL);
}
// 新的过期时间
long newExpireTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(time, unit);
Long currentExpireTime = (Long) redisTemplate.opsForValue().getAndSet(key, newExpireTime);
if (null == currentExpireTime) {
currentExpireTime = 0L;
}
if (currentExpireTime.equals(oldExpireTime)) {
// 获取到锁
redisTemplate.expire(key, time, unit);
return true;
}
} while (true);
} catch (Exception e) {
return false;
}
}
/**
* 获取缓存标识前缀
*
* @param typeName 类名
* @return 前缀
*/
protected final String getPrefix(String typeName) {
return typeName;
}
}
业务模块使用方法
@RedisLock(key = "test+#p0+#p1", expireTime = 30)
public void updateUser(String key, String nextKey, List<LeaderQuotaJson> listQuotaJsons) {
//业务代码逻辑
//update
//delete
//add
}
ps:如果有不理解可以参考 手写redis@Cacheable注解 支持过期时间设置 大体思路上一直,内部实现功能不同