• 基于Redis的分布式锁


    SETNX命令(SET if Not eXists)
    语法:SETNX key value
    功能:原子性操作,当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
    Expire命令
    语法:expire(key, expireTime)
    功能:key设置过期时间
    GETSET命令
    语法:GETSET key value
    功能:将给定 key 的值设为 value ,并返回 key 的旧值 (old value),当 key 存在但不是字符串类型时,返回一个错误,当key不存在时,返回nil。
    GET命令
    语法:GET key
    功能:返回 key 所关联的字符串值,如果 key 不存在那么返回特殊值 nil 。
    DEL命令
    语法:DEL key [KEY …]
    功能:删除给定的一个或多个 key ,不存在的 key 会被忽略。

    第一种:使用redis的setnx()、expire()方法,用于分布式锁

    1. setnx(lockkey, 1) 如果返回0,则说明占位失败;如果返回1,则说明占位成功
    2. expire()命令对lockkey设置超时时间,为的是避免死锁问题。
    3. 执行完业务代码后,可以通过delete命令删除key。

    这个方案其实是可以解决日常工作中的需求的,但从技术方案的探讨上来说,可能还有一些可以完善的地方。比如,如果在第一步setnx执行成功后,在expire()命令执行成功前,发生了宕机的现象,那么就依然会出现死锁的问题

    第二种:使用redis的setnx()、get()、getset()方法,用于分布式锁,解决死锁问题

    1. setnx(lockkey, 当前时间+过期超时时间) ,如果返回1,则获取锁成功;如果返回0则没有获取到锁,转向2。
    2. get(lockkey)获取值oldExpireTime ,并将这个value值与当前的系统时间进行比较,如果小于当前系统时间,则认为这个锁已经超时,可以允许别的请求重新获取,转向3。
    3. 计算newExpireTime=当前时间+过期超时时间,然后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime。
    4. 判断currentExpireTime与oldExpireTime 是否相等,如果相等,说明当前getset设置成功,获取到了锁。如果不相等,说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败,或者继续重试。
    5. 在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后,比较自己的处理时间和对于锁设置的超时时间,如果小于锁设置的超时时间,则直接执行delete释放锁;如果大于锁设置的超时时间,则不需要再锁进行处理。
    import cn.com.tpig.cache.redis.RedisService;
    import cn.com.tpig.utils.SpringUtils;
    
    /**
     * Created by IDEA
     * User: shma1664
     * Date: 2016-08-16 14:01
     * Desc: redis分布式锁
     */
    public final class RedisLockUtil {
    
        private static final int defaultExpire = 60;
    
        private RedisLockUtil() {
            //
        }
    
        /**
         * 加锁
         * @param key redis key
         * @param expire 过期时间,单位秒
         * @return true:加锁成功,false,加锁失败
         */
        public static boolean lock(String key, int expire) {
    
            RedisService redisService = SpringUtils.getBean(RedisService.class);
            long status = redisService.setnx(key, "1");
    
            if(status == 1) {
                redisService.expire(key, expire);
                return true;
            }
    
            return false;
        }
    
        public static boolean lock(String key) {
            return lock2(key, defaultExpire);
        }
    
        /**
         * 加锁
         * @param key redis key
         * @param expire 过期时间,单位秒
         * @return true:加锁成功,false,加锁失败
         */
        public static boolean lock2(String key, int expire) {
    
            RedisService redisService = SpringUtils.getBean(RedisService.class);
    
            long value = System.currentTimeMillis() + expire;
            long status = redisService.setnx(key, String.valueOf(value));
    
            if(status == 1) {
                return true;
            }
            long oldExpireTime = Long.parseLong(redisService.get(key, "0"));
            if(oldExpireTime < System.currentTimeMillis()) {
                //超时
                long newExpireTime = System.currentTimeMillis() + expire;
                long currentExpireTime = Long.parseLong(redisService.getSet(key, String.valueOf(newExpireTime)));
                if(currentExpireTime == oldExpireTime) {
                    return true;
                }
            }
            return false;
        }
    
        public static void unLock1(String key) {
            RedisService redisService = SpringUtils.getBean(RedisService.class);
            redisService.del(key);
        }
    
        public static void unLock2(String key) {    
            RedisService redisService = SpringUtils.getBean(RedisService.class);    
            long oldExpireTime = Long.parseLong(redisService.get(key, "0"));   
            if(oldExpireTime > System.currentTimeMillis()) {        
                redisService.del(key);    
            }
       }
    
    }
    public void drawRedPacket(long userId) {
        String key = "draw.redpacket.userid:" + userId;
    
        boolean lock = RedisLockUtil.lock2(key, 60);
        if(lock) {
            try {
                //领取操作
            } finally {
                //释放锁
                RedisLockUtil.unLock(key);
            }
        } else {
            new RuntimeException("重复领取奖励");
        }
    }
  • 相关阅读:
    闭包的一个经典例子
    手机归属地查询API
    安卓模拟器导入通讯录
    使用RazorGenerator实现项目模块分离
    .met mvc 一种判断是否已登录的方式
    stl 空间配置器理解
    STL 生成全排列
    KMP算法理解
    解决八皇后问题,递归与非递归方式两种
    获取第K大的数
  • 原文地址:https://www.cnblogs.com/pypua/p/11061055.html
Copyright © 2020-2023  润新知