Redisson可重入锁原理

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redisson提供的分布式锁是可重入的,它使用的是Hset命令和Hash数据结构。

一、Redisson可重入锁原理

1、可重入:利用hash结构记录线程id和重入次数。

2、可重试:利用信号量和PubSub功能实现等待、唤醒,获取

锁失败的重试机制。

3、超时续约:利用watchDog,每隔一段时间(releaseTime

/3),重置超时时间。
640 (4).png
调用redisson.lock()方法会在redis中存储一个hash数据结构,key为锁的名称,value中的field为当前操作的线程id,value为锁重入的次数。

640 (5).png
代码测试:


private  RLock rLock=null;
    @ApiOperation(value="测试分布式锁的可重入锁原理", notes="testRedisson02")
    @GetMapping("/testRedisson02")
    public String testRedisson02() throws InterruptedException {
        //1、获取锁(可重入),并指定锁的名称
        rLock=redissonClient.getLock("lock:testRedisson02");
        //2、尝试获取锁,参数分别是:waitTime:获取锁的最大等待时间(期间会重试)
        // leaseTime:锁自动释放时间  TimeUnit:时间单位
        //tryLock(long waitTime, long leaseTime, TimeUnit unit)
        boolean isLock=rLock.tryLock(1,100, TimeUnit.SECONDS);
        //3、判断锁获取成功及释放
        if(isLock){
            try {
                log.info("执行正常的业务02......");
                this.testRedisson03();
            }catch (Exception e){
                log.info("锁获取异常02e:"+e);
            }finally {
                //锁未关闭,则手动释放锁
                if(rLock.isLocked()){
                    rLock.unlock();
                    log.info("释放锁成功02");
                }
            }
        }
        return "true";
    }

    public void testRedisson03() throws InterruptedException {
        //2、尝试获取锁,参数分别是:waitTime:获取锁的最大等待时间(期间会重试)
        // leaseTime:锁自动释放时间  TimeUnit:时间单位
        //tryLock(long waitTime, long leaseTime, TimeUnit unit)
        boolean isLock=rLock.tryLock(1,100, TimeUnit.SECONDS);
        //3、判断锁获取成功及释放
        if(isLock){
            try {
                log.info("执行正常的业务03......");
            }catch (Exception e){
                log.info("锁获取异常03e:"+e);
            }finally {
                //锁未关闭,则手动释放锁
                if(rLock.isLocked()){
                    rLock.unlock();
                    log.info("释放锁成功03");
                }
            }
        }
    }

输出结果:
640 (6).png
具体分析如下:
640 (7).png
在上面的testRedisson02方法中,当外层testRedisson02方法加锁之后,会获取当前的线程标识存入field字段,并将value+1;当内层testRedisson03方法再次加这个锁,会先判断当前线程与field中存的线程是否是一样的,如果是一样的,value+1;此时value为2。如果要解锁,先要判断锁是否是自己的(比对key和field字段),如果是,则value-1。
640 (8).png
获取锁的Lua脚本源码

return evalWriteAsync(getName(), LongCodec.INSTANCE, command,
                "if (redis.call('exists', KEYS[1]) == 0) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "return redis.call('pttl', KEYS[1]);",

释放锁的Lua脚本源码:

return evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
                        "return nil;" +
                        "end; " +
                        "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
                        "if (counter > 0) then " +
                        "redis.call('pexpire', KEYS[1], ARGV[2]); " +
                        "return 0; " +
                        "else " +
                        "redis.call('del', KEYS[1]); " +
                        "redis.call('publish', KEYS[2], ARGV[1]); " +
                        "return 1; " +
                        "end; " +
                        "return nil;",

二、总结
640 (9).png
1、不可重入Redis分布式锁:

原理:利用setnx的互斥性;利用ex避免死锁;释放锁时判

断线程标识。

缺陷:不可重入、无法重试、锁超时失效。

2、可重入的Redis分布式锁:

原理:利用hash结构,记录线程标示和重入次数;利用

watchDog延续锁时间;利用信号量控制锁重试等待。

缺陷:redis宕机引起锁失效问题。--主从一致性问题

3、RedissonmultiLock:

原理:多个独立的Redis节点,必须在所有节点都获取重入

锁,才算获取锁成功。

缺陷:运维成本高、实现复杂。

更多详细资料,请关注个人微信公众号或搜索“程序猿小杨”添加。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
Java
ReentrantLock(可重入锁)源码解读与使用
ReentrantLock(可重入锁)源码解读与使用
|
6月前
|
存储 Java
StampedLock(戳记锁)源码解读与使用
StampedLock(戳记锁)源码解读与使用
|
6月前
|
安全 Java 测试技术
ReentrantReadWriteLock(可重入读写锁)源码解读与使用
ReentrantReadWriteLock(可重入读写锁)源码解读与使用
|
缓存 NoSQL Redis
redisson实现分布式锁
redisson实现分布式锁
64 1
|
6月前
|
监控 NoSQL 算法
Redisson–红锁(Redlock)–使用/原理
Redisson–红锁(Redlock)–使用/原理
340 0
|
NoSQL Java 测试技术
redisson中的分布式锁解读(下)
redisson中的分布式锁解读(下)
|
分布式计算 NoSQL Java
Redisson的使用与可重入锁
介绍Redisson以及Redisson中的可重入锁的原理
741 0
|
Java
Redisson 分布式锁源码 10:读写锁
Redisson 还支持可重入读写锁,允许在分布式场景下,同时有多个读锁和一个写锁处于加锁状态。
328 0
|
NoSQL Java Redis
Redisson 分布式锁源码 03:可重入锁互斥
看过可重入锁的 Lua 脚本,已经可以知道当锁存在时,是会加锁失败的。 下面看一下,加锁失败之后是如何处理的呢?
256 0
|
NoSQL 算法 Java
Redisson 分布式锁源码 01:可重入锁加锁
相信小伙伴都是使用分布式服务,那一定绕不开分布式服务中数据并发更新问题! 单系统很容易想到 Java 的各种锁,像 synchronize、ReentrantLock 等等等,那分布式系统如何处理?
257 0