• springMVC 实现redis分布式锁


    1.先配置spring-data-redis

    首先是依赖

        <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-redis</artifactId>
                <version>1.8.4.RELEASE</version>
            </dependency>

    redisconfig 配置类

    @Configuration
    @PropertySource("classpath:irongbei.properties")
    public class RedisConfig extends JCacheConfigurerSupport {
        @Autowired
        private Environment environment;
    
        @Bean
        public RedisConnectionFactory redisConnectionFactory() {
            JedisConnectionFactory fac = new JedisConnectionFactory();
            fac.setHostName(environment.getProperty("redis.host"));
            fac.setPort(Integer.parseInt(environment.getProperty("redis.port")));
            fac.setPassword(environment.getProperty("redis.password"));
            fac.setTimeout(Integer.parseInt(environment.getProperty("redis.timeout")));
    //        fac.getPoolConfig().setMaxIdle(Integer.parseInt(environment.getProperty("redis.maxIdle")));
    //        fac.getPoolConfig().setMaxTotal(Integer.parseInt(environment.getProperty("redis.maxTotal")));
    //        fac.getPoolConfig().setMaxWaitMillis(Integer.parseInt(environment.getProperty("redis.maxWaitMillis")));
    //        fac.getPoolConfig().setMinEvictableIdleTimeMillis(
    //                Integer.parseInt(environment.getProperty("redis.minEvictableIdleTimeMillis")));
    //        fac.getPoolConfig()
    //                .setNumTestsPerEvictionRun(Integer.parseInt(environment.getProperty("redis.numTestsPerEvictionRun")));
    //        fac.getPoolConfig().setTimeBetweenEvictionRunsMillis(
    //                Integer.parseInt(environment.getProperty("redis.timeBetweenEvictionRunsMillis")));
    //        fac.getPoolConfig().setTestOnBorrow(Boolean.parseBoolean(environment.getProperty("redis.testOnBorrow")));
    //        fac.getPoolConfig().setTestWhileIdle(Boolean.parseBoolean(environment.getProperty("redis.testWhileIdle")));
            return fac;
        }
    
        @Bean
        public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<String, String> redis = new RedisTemplate<>();
            // 设置redis的String/Value的默认序列化方式
            DefaultStrSerializer defaultStrSerializer = new DefaultStrSerializer();
            redis.setKeySerializer(defaultStrSerializer);
            redis.setValueSerializer(defaultStrSerializer);
            redis.setHashKeySerializer(defaultStrSerializer);
            redis.setHashValueSerializer(defaultStrSerializer);
            redis.setConnectionFactory(redisConnectionFactory);
            redis.afterPropertiesSet();
            return redis;
        }
    }
    
     class DefaultStrSerializer implements RedisSerializer<Object> {
        private final Charset charset;
    
        public DefaultStrSerializer() {
            this(Charset.forName("UTF8"));
        }
    
        public DefaultStrSerializer(Charset charset) {
            Assert.notNull(charset, "Charset must not be null!");
            this.charset = charset;
        }
    
    
        @Override
        public byte[] serialize(Object o) throws SerializationException {
            return o == null ? null : String.valueOf(o).getBytes(charset);
        }
    
        @Override
        public Object deserialize(byte[] bytes) throws SerializationException {
            return bytes == null ? null : new String(bytes, charset);
    
        }
    }
    View Code

    redisLock类 分布式锁实现的类

    @Component
    public class RedisLock {
    
        private static final Logger log = LoggerFactory.getLogger(RedisLock.class);
    
        @Autowired
        private RedisTemplate<String, String> redisTemplate;
    
        /**
         *
         * @param key
         * @param value 当前时间+超时时间
         * @return
         */
        public boolean lock(String key,String value){
    
            if(redisTemplate.opsForValue().setIfAbsent(key,value)){
                log.info(" [Redis分布式锁] key:[{}] 获取到锁",key);
                return true;
            }
            //oldvalue  俩个线程都返回A
            String currentValue = redisTemplate.opsForValue().get(key);
            //如果锁过期->这里如果一个key的value时间是小于当前当前时间 那就是过期了,如果大于当前时间才没有过期
            if(StringUtils.isNotEmpty(currentValue) && Long.parseLong(currentValue) <System.currentTimeMillis()){
    
                //获取上一个锁的时间
                //第一个线程获取上一个oldvalue 然后设置一个新的值进去 第二个线程就获取到是新的值.
                String oldValue = redisTemplate.opsForValue().getAndSet(key,value);
                //3 这一步就只有第一个线程能匹配到了 第二个线程就获取不到了
                if(StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue)){
                    log.info(" [Redis分布式锁] key:[{}] 获取到锁",key);
                    return true;
                }
    
            }
    
            return false;
    
        }
    
        /**
         * 解锁
         * @param key
         * @param value
         */
        public void unlock(String key ,String value){
            try {
                String currentValue = redisTemplate.opsForValue().get(key);
                if(StringUtils.isNotEmpty(currentValue) && currentValue.equals(value)){
                    redisTemplate.opsForValue().getOperations().delete(key);
                }
            } catch (Exception e) {
                log.error(" [redis分布式锁] 解锁异常, {} ",e);
            }
    
    
        }
    }

    测试类:

    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @ContextConfiguration(locations = {"/spring-context.xml","/spring-context-jedis.xml","/spring-context-shiro.xml"})
    public class RedisLockTest {
        @Resource(name = "threadPoolTaskExecutor")
        private ThreadPoolExecutor taskExecutor;
    
    
        @Autowired
        private RedisLock redisLock;
        private long timeout = 5*1000;
    
        private static  final Logger logger =LoggerFactory.getLogger(RedisLockTest.class);
    
        @Test
        public void  test (){
            for (int i = 0; i <200 ; i++) {
                taskExecutor.execute(()->{
                    String time = String.valueOf(System.currentTimeMillis()+timeout);
                    if(!redisLock.lock("testlock",time)){
                        logger.info("哎呦喂..人太多了 在排队中..");
                       return;
                    }else {
                        try {
                            Thread.sleep(4000);
                            redisLock.unlock("testlock",time);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
    
                });
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
    
        }
    }

     补充 

    和同事讨论,结果确实如同事所说,解锁的时候有些不严谨,因为是分俩步操作的.可能会在del之前被别人获取到锁然后再del删除掉别人获取的锁.下面是新的解锁方式,目前有些公司redis服务器不支持这样的命令

      为什么使用lua语言在操作Redis 主要是保证操作的原子性.一步操作

      但是使用lua要非常小心,如果脚本错误可能会阻塞整个Redis实例

     private static final Long SUCCESS = 1L;
        /**
         * 释放锁
         * @param key
         * @param value
         * @return
         */
        public  boolean releaseLock(String key, String value) {
            key =prifix+key;
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    
            RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
    
            try {
                Object result = redisTemplate.execute(redisScript, Collections.singletonList(key), value);
                System.out.println(result);
                if (SUCCESS.equals(result)) {
                    log.info("[redis 分布式锁解锁成功] key:[{}] 结果:{}",key,result);
                    return true;
                }
                log.info("[redis 分布式锁解锁失败] key:[{}]  结果{}",key,result);
            } catch (Exception e) {
                log.info("[redis 分布式锁解锁失败]  key:[{}] msg:{}",key,e.getMessage());
                e.printStackTrace();
            }
    
            return false;
        }

     测试代码

        @Test
        public void testPrefix(){
            redisLock.lock("test","10000");
            redisLock.lock("test1","10000");
    //        redisLock.lock("test2","10000");
    //        redisLock.lock("test3","10000");
            redisLock.releaseLock("test","10000");
            redisLock.releaseLock("test1","10002");
        }
  • 相关阅读:
    Java随笔
    Java随笔
    Java随笔
    CF1271D Portals(反悔贪心)
    CF938D Buy a Ticket(最短路)
    CF1117C Magic Ship(二分)
    HDU6820 Tree(树形dp)
    P2393 美味(主席树+贪心)
    HDU6831 Fragrant numbers(区间dp)
    HDU6832 A Very Easy Graph Problem(生成树)
  • 原文地址:https://www.cnblogs.com/bj-xiaodao/p/10734464.html
Copyright © 2020-2023  润新知