美文网首页
《redis in action》——锁

《redis in action》——锁

作者: 墨荒 | 来源:发表于2018-06-07 18:54 被阅读0次

    redis测试项目搭建

    新建项目

    http://start.spring.io/填写项目标识、添加redis依赖,下载项目用idea打开

    配置redis

    • 启动本地redis服务器
    • 在application.properties文件添加配置
    spring.redis.host=127.0.0.1
    # Redis服务器连接端口
    spring.redis.port=6379
    # Redis服务器连接密码(默认为空)
    spring.redis.password=
    # 连接超时时间(毫秒)
    spring.redis.timeout=30000
    
    • 测试redis是否可用
    public class RedisConnectTest extends RedisApplicationTests{
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        @Test
        public void testRedis() throws Exception {
            stringRedisTemplate.opsForValue().set("a", String.valueOf(1));
            System.out.println(stringRedisTemplate.opsForValue().get("a"));
        }
    }
    

    场景

    定义锁的工具类接口

    public interface LockHelper {
        void lock(String key);
        void unLock(String key);
    }
    
    

    调用的时候注意在finally的时候释放锁,避免业务执行失败时没释放所导致死锁

                try {
                    lockHelper.lock(account.id);
                    super.run();
                }finally {
                    lockHelper.unLock(account.id);
                }
    
    

    本地锁

    ConcurrentHashMap+ReentrantLock:

    @Component("LocalLockHelper")
    public class LocalLockHelper implements LockHelper {
        private ConcurrentHashMap<String,Lock> lockMap = new ConcurrentHashMap<>();
        @Override
        public void lock(String key) {
            Lock lock = lockMap.get(key);
            if(lock == null){
                Lock newLock = new ReentrantLock();
                Lock existLock = lockMap.putIfAbsent(key,newLock);
                //锁不存在的话就用新锁,存在的话用旧锁
                lock = existLock == null?newLock:existLock;
            }
            lock.lock();
        }
        @Override
        public void unLock(String key) {
            Lock lock = lockMap.get(key);
            if(lock != null){
                lock.unlock();
            }
        }
    }
    
    

    redis锁

    加锁多了个支持锁过期的方法,防止分布式情况下一些客户端没有(成功)释放锁,甚至在第一次申请锁的时候对应的key已经存在。

    @Component("RedisLockHelper")
    public class RedisLockHelper implements LockHelper,ExpireLock{
        @Autowired
        private StringRedisTemplate redisTemplate;
        private Map<String, String> keyMap = new HashMap<>();
    
        @Override
        public void lock(String key) {
            BoundValueOperations<String,String> boundValueOperations =
                    this.redisTemplate.boundValueOps(getLockKey(key));
            while (true) {
                if (boundValueOperations.setIfAbsent(this.getRequestId())) {
                    break;
                }else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        /**
         *设置持有锁的超时时间,防止某些客户端未释放锁
         */
        @Override
        public void lock(String key, long expireTimeMills) {
            BoundValueOperations<String,String> boundValueOperations =
                    this.redisTemplate.boundValueOps(getLockKey(key));
            while (true) {
                if (boundValueOperations.setIfAbsent(this.getRequestId())) {
                    boundValueOperations.expire(expireTimeMills, TimeUnit.MILLISECONDS);
                    break;
                }else {
                    long wait;
                    if (boundValueOperations.getExpire() == -1) {
                        System.out.println(key+"|"+Thread.currentThread().getName()+"设置锁超时时长");
                        //防止未释放锁
                        wait = expireTimeMills;
                        boundValueOperations.expire(expireTimeMills, TimeUnit.MILLISECONDS);
                    }else{
                        wait = 100;
                    }
                    try {
                        Thread.sleep(wait);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        @Override
        public void unLock(String key) {
            String lk = getLockKey(key);
            BoundValueOperations<String,String> boundValueOperations =
                    this.redisTemplate.boundValueOps(lk);
            if(this.getRequestId().equals(boundValueOperations.get())) {
    //            释放锁的时候检测当前值是否为请求id,防止被其他请求释放,这个地方应该用lua脚本 把检查和删除 做成一个有原子性的方法或者命令
                redisTemplate.delete(lk);
            }
        }
    
        private String getRequestId() {
            //多实例情况下需要设置为http requestId之类的
            return String.valueOf(Thread.currentThread().getId());
        }
    
        private String getLockKey(String key) {
            String t = keyMap.get(key);
            if(t == null){
                t = "lock:"+key;
                keyMap.put(key,t);
            }
            return t;
        }
    }
    

    相关文章

      网友评论

          本文标题:《redis in action》——锁

          本文链接:https://www.haomeiwen.com/subject/hetrsftx.html