注解方式集成redis分布式锁

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xiewenfeng520/article/details/86641123

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注解 支持过期时间设置  大体思路上一直,内部实现功能不同

展开阅读全文

没有更多推荐了,返回首页