zoukankan      html  css  js  c++  java
  • redis 分布式锁 spring boot

    import java.util.concurrent.TimeUnit;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.ValueOperations;
    import org.springframework.stereotype.Component;
    
    import com.alibaba.fastjson.JSON;
    
    @Component
    public class RedisUtil {
    
        @Autowired
        private RedisTemplate<String, String> template;
        /**
         * 
         * @param key
         * @param value
         * @param timeOut 秒
         */
        public void setValue(final String key,Object value,long timeOut) {
            ValueOperations<String, String> ops = this.template.opsForValue();
            ops.set(key, JSON.toJSONString(value), timeOut, TimeUnit.SECONDS);
        }
        
        public void removeKey(final String key) {
            this.template.delete(key);
        }
        /**
         * 更新过期时间
         * @param key
         * @param timeOut (单位,秒)
         */
        public void expire(final String key,long timeOut) {
            template.expire(key, timeOut, TimeUnit.SECONDS);
        }
    
        
        public boolean getLock(String key) {
            String value = String.format("%s", System.currentTimeMillis()+90*1000);
            if(template.opsForValue().setIfAbsent(key, value)) {
                return true;
            }
            //currentValue=A   这两个线程的value都是B  其中一个线程拿到锁
            String currentValue = template.opsForValue().get(key);
            //如果锁过期
            if (!StringUtils.isEmpty(currentValue)
                    && Long.parseLong(currentValue) < System.currentTimeMillis()) {
                //获取上一个锁的时间
                String oldValue = template.opsForValue().getAndSet(key, value);
                if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
                    return true;
                }
            }
            return false;
        }
        
        /**
         * 解锁
         * @param key
         * @param value
         */
        public void removeLock(String key) {
            template.opsForValue().getOperations().delete(key);
        }
        
        public <T> T getValue(final String key,Class<T> clz) {
            ValueOperations<String, String> ops = this.template.opsForValue();
            String jsonStr = ops.get(key);
            if (jsonStr != null) {
                T t = JSON.parseObject(jsonStr, clz);
                return t;
            }
            return null;
        }
        
        public boolean hasKey(String key){
            return template.hasKey(key);
        }
    
    }

    测试类

    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import com.thgames.exception.BusinessException;
    import com.thgames.proxy.config.RedisUtil;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(classes=GiftApplication.class)
    @ActiveProfiles("dev")
    public class GameProxyApplicationTests2 {
    
        @Autowired private RedisUtil redisUtil;
        
        @Test
        public void skipTest() throws BusinessException {
            
            for(int i=0;i<20;i++) {
                new Thread() {
    
                    @Override
                    public void run() {
                        testlock("uid-a", "prefix_a");
                    }
                    
                }.start();
            }
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
        }
    
        public void testlock(String uid,String prefix) {
            boolean flag = false;
            try {
                if(flag=redisUtil.getLock(uid+prefix)) {
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName()+" getLock success");
                } else {
                    System.out.println(Thread.currentThread().getName()+" not getLock");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if(flag)redisUtil.removeLock(uid+prefix);
            }
        }
        
    }

    运行结果

    Thread-5 not getLock
    Thread-8 not getLock
    Thread-4 not getLock
    Thread-23 not getLock
    Thread-14 not getLock
    Thread-18 not getLock
    Thread-7 not getLock
    Thread-11 not getLock
    Thread-15 not getLock
    Thread-19 not getLock
    Thread-10 not getLock
    Thread-6 not getLock
    Thread-22 not getLock
    Thread-9 not getLock
    Thread-17 not getLock
    Thread-16 not getLock
    Thread-12 not getLock
    Thread-20 not getLock
    Thread-13 not getLock
    Thread-21 getLock success

    原理,redis使用的是单线程模型,setnx命令和getset命令保证原子性,线程安全,低版本setnx没有过期时间参数,为了避免死锁,所以得把过期时间当作value传过去,然后再用getset返回的旧值与当前值比较,相等则获得锁,类似CAS的原理

  • 相关阅读:
    绿有四季关于需求沟通
    模板引擎开发(二)值标签的处理
    Jquery插件浮动广告
    项目经理的“势能”培养
    模板引擎开发(一)
    c#读取apk 信息
    golang读取文件信息插入mongodb
    白话MongoDB(二)(转载)
    golang 读取mongob数据写入sqlserver
    golang 通用Contains方法
  • 原文地址:https://www.cnblogs.com/xiongjinpeng/p/12938362.html
Copyright © 2011-2022 走看看