redis分布式锁unlock方法

来源:互联网 发布:超级玛丽安卓源码 编辑:程序博客网 时间:2024/06/17 08:00

redis作为分布式锁的运用,网上有无数的案例,这里提供一个我自己设计的unlock解锁方案。

相对于加锁,解锁的过程相对简单,之前我项目里解锁就是直接delete lock_key,由于加锁的过程设置了超时时间,简单的delete lock_key显然有造成误删锁的风险(下面会具体介绍误删的原因)。本人梳理了下解锁的流程,设计了一个大部分情况下可以避免误删的解锁方案。

首先引用一个典型的加锁流程。利用的jedis客户端,其中setnx方法判断锁是否被其他线程占用,如果被占用的话也不是马上退出,而是比较占用的锁是否超时。这儿getSet方法用来避免多个线程同时删除过期锁,从而同时获取到锁的情况,保证只有一个线程能修改过期锁。这个过程网上有无数的案例,在此不作过多描述。下面分析该方法的unlock方法如何解锁。

/*    * 典型加锁流程    * */    public boolean acquireLock(String lock) {        boolean success = false;        Jedis jedis = pool.getResource();        //过期时间3分钟        long value = System.currentTimeMillis() + 3 * 60 * 1000 + 1;        long acquired = jedis.setnx(lock, String.valueOf(value));        //SETNX成功,则成功获取一个锁        if (acquired == 1)            success = true;            //SETNX失败,说明锁仍然被其他对象保持,检查其是否已经超时        else {            long oldValue = Long.valueOf(jedis.get(lock));            //超时            if (oldValue < System.currentTimeMillis()) {                String getValue = jedis.getSet(lock, String.valueOf(value));                // 获取锁成功                if (Long.valueOf(getValue) == oldValue)                    success = true;                    // 已被其他进程捷足先登了                else                    success = false;            }            //未超时,则直接返回失败            else                success = false;        }        pool.returnResource(jedis);        return success;    }

关于unlock方法,如果直接删除lock_key,显然可能存在这样的错误:

当T1加锁成功,但执行过程被挂起,导致执行时间超过3分钟,另一个线程T2修改了T1的锁过期时间,此时T2加锁成功。当T1执行到解锁的流程,如果T1直接delete lock_key,删除的是T2修改过的lock_key,这时候T2未必执行完毕。如果再有T3过来,将直接加锁成功,导致T2和T3并发。

问题的关键是,T1解锁的时候,没有验证删除的是自己锁。为了让T1线程能“认识”自己加的锁,我们修改了lock方法,返回加锁的时间戳,用于unlock判断。加锁代码:

 /*    * 加锁成功返回时间戳,加锁失败返回空对象null    * */    public String lock(String lock) {        Jedis jedis = pool.getResource();        long value = System.currentTimeMillis() + 3 * 60 * 1000 + 1;        String timeStamp = String.valueOf(value);        long acquired = jedis.setnx(lock, timeStamp);        //SETNX成功,则成功获取一个锁        if (acquired == 1) {            return timeStamp;        } else {            long oldValue = Long.valueOf(jedis.get(lock));            //超时            if (oldValue < System.currentTimeMillis()) {                String getValue = jedis.getSet(lock, timeStamp);                // 这种情况不能加锁                if (Long.valueOf(getValue) != oldValue) {                    timeStamp = null;                }            } else {                //锁未超时,也不能加锁                timeStamp = null;            }        }        pool.returnResource(jedis);        //只有加锁成功情况,才返回了加锁的时间戳        return timeStamp;    }
lock方法可以返回时间戳,当调用unlock方法时,用这个时间戳作为验证的参数

/*    * 解锁要验证加锁返回的时间戳    * */    public void unlock(String lock, String timeStamp) {        Jedis jedis = pool.getResource();        String value = jedis.get(lock); .......................................(1)        long expireTime = Long.parseLong(value);        if (System.currentTimeMillis() > expireTime                && value.equals(timeStamp)) {            jedis.del(lock);    ...............................................(2)        }        pool.returnResource(jedis);    }

这儿解释一下delete lock_key的条件:

1、当锁未超时(小于currentTimeMillis)

2、当锁的value和timeStamp相等

为什么要验证锁未超时?因为如果锁已经超时,就算value和timeStamp相等,即线程自己加的锁,如果直接删除这个超时的锁,可能删除的是另一个线程的锁,具体过程像这样:

(1)T1线程查看锁是否是自己的timeStamp:

String value = jedis.get(lock);

if(value.equals(timeStamp)) ==> true

//判断timeStamp是不是和value相同,结果相同,因此T1线程正准备删除key

(2)这时候T2线程过来获取锁,由于T1的锁已经超时,T2直接加锁成功了,这时候lock的value其实不在是timeStamp

(3)T1执行到删除的命令,然后悲剧发生,T2的lock_key被删掉了。。。

整个错误的核心在于,unlock方法里面(1)、(2)两个步骤之间,不能有别的线程加锁。

按照这个思路,redis的watch似乎可以更简洁的处理lock和unlock的过程。但据说watch涉及到redis事务开销,很少有用watch实现分布锁。如果读者有更好的方式,欢迎向博主推荐

(个人原创,转载请注明出处)


阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 魔术贴裁剪机 魔术贴裁切机 照片裁切机 导光板裁切机 书本裁切机 剪切板怎么打开 手机剪切板记录在哪找 手机剪切板 如何清空剪切板 手机的剪切板在哪里打开 手机剪切板在哪里找 手机剪切板在哪 打开剪切板的快捷键 自己剪刘海儿的窍门 剪刘海教程 剪刘海技巧 剪空气刘海 刘海怎么剪好看 梦见剪刘海 怎样自己剪刘海 怎样剪刘海 如何剪刘海 剪碎刘海 平刘海怎么剪好看 剪个空气刘海多少钱 圆脸适合剪刘海吗 如何剪平刘海 梦见剪刘海预示着什么 女生斜刘海怎么剪 怎么剪薄刘海 如何自己剪齐刘海 薄刘海怎么剪 自己剪空气刘海教程 自己怎么剪空气刘海 怎么剪好看的刘海 齐刘海自己怎么剪 剪个刘海多少钱 自剪刘海 怎么自己剪空气刘海 韩式空气刘海怎么剪 男生斜刘海怎么剪