博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Redis实现锁
阅读量:6856 次
发布时间:2019-06-26

本文共 3064 字,大约阅读时间需要 10 分钟。

hot3.png

Redis实现锁

虽然很多的Redis用户都对锁(lock),加锁(locking)以及锁超时(lock timeouts)有所了解,但大部分使用Redis实现的锁只是基本上正确,它们发生故障的时间和方式通常都难以预料。下面列出了一些导致锁出现不正确行为的原因,以及锁在不正确运行时的症状:

  • 持有锁的进程因为操作时间过长而导致锁被自动释放,但进程本身并不知道,甚至还可能会错误的释放掉了其他进程持有的锁。

  • 一个持有锁并打算执行长时间操作的进程已经崩溃,但其他想要获取锁的进程不知道哪个进程持有着锁,只能白白浪费时间等待锁被释放

  • 在一个进程持有的锁过期之后,其他多个进程同时尝试去获取锁,并且都获得了锁。

  • 上面提到的第一个种情况和第三种情况同时出现,导致有多个进程获得了锁,而每个进程都以为自己是唯一一个获得锁的进程。

以上几种情况是redis锁使用过程中有可能出现的问题,在redis锁的使用过程中应该要注意。

Redis实现基本的加锁和锁的释放

为了对数据进行排他性访问,程序首先要做的就是获取锁。SETNX命令天生就适合用来实现锁的获取功能,这个命令只在键不存在的情况下为键设置值,而锁要做的就是将一个随机生成的128位UUID设置为键的值,并使用这个值来防止锁被其他进程取得。

如果程序在尝试获取锁的时候失败,那么它将不断的进行重试,知道成功的取得锁获取超时,代码如下,

/** * 获取锁 * * @param conn * @param lockName * @param acquireTimeout 获取锁直到超时 * @return */public String acquireLock(Jedis conn, String lockName, long acquireTimeout) {    String identifier = UUID.randomUUID().toString();    long end = System.currentTimeMillis() + acquireTimeout;    //获取锁的过程超时后,直接返回null    while (System.currentTimeMillis() < end) {        if (conn.setnx("lock:" + lockName, identifier) == 1) { //key-value设置成功            return identifier; //返回锁的标识符        }        //如果没有设置成功,即获取锁失败,sleep一毫秒继续重试,直到获取锁的过程超时        try {            Thread.sleep(1);        } catch (InterruptedException ie) {            Thread.currentThread().interrupt();        }    }    return null;}

以上代码实现了加锁过程,那么如何释放锁,

/** * 释放锁 * * @param conn * @param lockName * @param identifier * @return */public boolean releaseLock(Jedis conn, String lockName, String identifier) {    String lockKey = "lock:" + lockName;    while (true) {        conn.watch(lockKey);        //判断锁的标识,如果相等,说明标识符对应的线程正在持有该锁,可以释放        if (identifier.equals(conn.get(lockKey))) {            Transaction trans = conn.multi();            trans.del(lockKey);            List results = trans.exec();            if (results == null) {                continue;            }            return true;        }        //不再监视lockKey        conn.unwatch();        break;    }    //释放锁失败    return false;}

带有超时限制的锁

如下代码所示,

/** * 获取锁,锁有超时特性 * * @param conn * @param lockName * @param acquireTimeout * @param lockTimeout * @return */public String acquireLockWithTimeout(        Jedis conn, String lockName, long acquireTimeout, long lockTimeout) {    String identifier = UUID.randomUUID().toString();    String lockKey = "lock:" + lockName;    int lockExpire = (int) (lockTimeout / 1000);    long end = System.currentTimeMillis() + acquireTimeout;    //循环获取锁,直到获取成功或超时    while (System.currentTimeMillis() < end) {        if (conn.setnx(lockKey, identifier) == 1) { //设置成功            //设置成功后设置锁的过期时间            conn.expire(lockKey, lockExpire);            return identifier;        }        //当获取锁失败后,检查过期时间        //如果返回-1 表示该锁没有设置过期        if (conn.ttl(lockKey) == -1) {            //设置一个过期时间            conn.expire(lockKey, lockExpire);        }        try {            Thread.sleep(1);        } catch (InterruptedException ie) {            Thread.currentThread().interrupt();        }    }    // null indicates that the lock was not acquired    return null;}

======END======

转载于:https://my.oschina.net/xinxingegeya/blog/659521

你可能感兴趣的文章
手机自适应web
查看>>
第 6 章 存储 - 042 - 用 volume container 共享数据
查看>>
linux学习指南
查看>>
牛逼的vscode的设置
查看>>
DP 恢复命令omnir
查看>>
nagios
查看>>
3.文件属性,权限,正则表达式
查看>>
大数据从小数据开始
查看>>
我在老男孩学Python的日子_day2
查看>>
jenkins自动化部署
查看>>
优达学城数据分析师纳米学位——P5项目知识点整理贝叶斯规则
查看>>
python数据结构与算法(7)
查看>>
Java的新项目学成在线笔记-day6(十二)
查看>>
Program ape and siege lion
查看>>
阿里程序员带你全面深入了解正则表达式
查看>>
Vipkid怎么样,说说孩子在vipkid的学后体验。
查看>>
基于回归幅度的反转交易策略
查看>>
《萌萌守卫塔防》隐私政策
查看>>
容器(docker)中运行java需关注的几个小问题
查看>>
ipad安卓协议最新6.7.4
查看>>