我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Redis如何实现可重入锁的设计

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Redis如何实现可重入锁的设计

这篇文章主要介绍Redis如何实现可重入锁的设计,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

但是仍然有些场景是不满⾜的,例如⼀ 个⽅法获取到锁之后,可能在⽅法内调这个⽅法此时就获取不到锁了。这个时候我们就需要把锁改进成可 重⼊锁了。 重⼊锁,指的是以线程为单位,当⼀个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,⽽其 他的线程是不可以的。可重⼊锁的意义在于防⽌死锁。 实现原理是通过为每个锁关联⼀个请求计数器和⼀个占有它的线程。当计数为 0 时,认为锁是未被占有 的;线程请求⼀个未被占有的锁时,JVM 将记录锁的占有者,并且将请求计数器置为 1 。 如果同⼀个线程再次请求这个锁,计数将递增;每次占⽤线程退出同步块,计数器值将递减。直到计数器 为 0, 锁被释放。 关于⽗类和⼦类的锁的重⼊:⼦类覆写了⽗类的 synchonized ⽅法,然后调⽤⽗类中的⽅法,此时如果没有重⼊的锁,那么这段代码将产⽣死锁。

代码演示

不可重⼊

  • 不可重⼊锁
    Redis如何实现可重入锁的设计

  • 使用不可重入锁Redis如何实现可重入锁的设计
    当前线程执⾏ call() ⽅法⾸先获取 lock,接下来执⾏ inc() ⽅法就⽆法执⾏ inc() 中的逻辑,必须先释放锁。该例很好的说明了不可重⼊锁。

可重入锁

  • 锁实现
    Redis如何实现可重入锁的设计

  • 锁使用Redis如何实现可重入锁的设计

可重⼊意味着线程可进⼊它已经拥有的锁的同步代码块。

设计两个线程调⽤ call() ⽅法,第⼀个线程调⽤ call() ⽅法获取锁,进⼊ lock() ⽅法,由于初始 lockedBy 是 null,所以不会进⼊ while ⽽挂起当前线程,⽽是增量 lockedCount 并记录 lockBy 为第 ⼀个线程。

接着第⼀个线程进⼊ inc() ⽅法,由于同⼀进程,所以不会进⼊ while ⽽挂起,接着增量 lockedCount,当第⼆个线程尝试 lock,由于 isLocked=true, 所以他不会获取该锁,直到第⼀个线程调⽤两次 unlock() 将 lockCount 递减为 0,才将标记为 isLocked 设置为 false。

设计思路

假设锁的key为“lock”,hashKey是当前线程的id:“threadId”,锁自动释放时间假设为20。

获取锁

判断lock是否存在 EXISTS lock

  • 不存在,则自己获取锁,记录重入层数为1.

  • 存在,说明有人获取锁了,继续判断是不是自己的锁,即判断当前线程id作为hashKey是否存在:HEXISTS lock threadId

    • 不存在,说明锁已经有了,且不是自己获取的,锁获取失败.

    • 存在,说明是自己获取的锁,重入次数+1: HINCRBY lock threadId 1 ,最后更新锁自动释放时间, EXPIRE lock 20
      Redis如何实现可重入锁的设计

释放锁

判断当前线程id作为hashKey是否存在: HEXISTS lock threadId

  • 不存在,说明锁已失效

  • 存在,说明锁还在,重入次数减1: HINCRBY lock threadId -1 ,

    • 获取新的重入次数,判断重入次数是否为0,为0说明锁全部释放,删除key: DEL lock

因此,存储在锁中的信息就必须包含:key、线程标识、重入次数。不能再使用简单的 key-value 结构, 这里推荐使用 hash 结构。而且要让所有指令都在同一个线程中操作,那么使用 lua 脚本。

lua 脚本

lock.lua

local key = KEYS[1]; -- 第1个参数,锁的keylocal threadId = ARGV[1]; -- 第2个参数,线程唯一标识local releaseTime = ARGV[2]; -- 第3个参数,锁的自动释放时间if(redis.call('exists', key) == 0) then -- 判断锁是否已存在
    redis.call('hset', key, threadId, '1'); -- 不存在, 则获取锁
    redis.call('expire', key, releaseTime); -- 设置有效期
    return 1; -- 返回结果end;if(redis.call('hexists', key, threadId) == 1) then -- 锁已经存在,判断threadId是否是自己    
    redis.call('hincrby', key, threadId, '1'); -- 如果是自己,则重入次数+1
    redis.call('expire', key, releaseTime); -- 设置有效期
    return 1; -- 返回结果end;return 0; -- 代码走到这里,说明获取锁的不是自己,获取锁失败

unlock.lua

-- 锁的 keylocal key = KEYS[1];-- 线程唯一标识local threadId = ARGV[1];-- 判断当前锁是否还是被自己持有if (redis.call('hexists', key, threadId) == 0) then--     如果已经不是自己,则直接返回
    return nil;end;-- 是自己的锁,则重入次数减一local count = redis.call('hincrby', key, threadId, -1);-- 判断重入次数是否已为0if (count == 0) then--     等于 0,说明可以释放锁,直接删除
    redis.call('del', key);
    return nil;end;

在项目中集成

编写 RedisLock 类

@Getter@Setterpublic class RedisLock {

    private RedisTemplate redisTemplate;
    private DefaultRedisScript<Long> lockScript;
    private DefaultRedisScript<Object> unlockScript;

    public RedisLock(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
        // 加载释放锁的脚本
        this.lockScript = new DefaultRedisScript<>();
        this.lockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lock.lua")));
        this.lockScript.setResultType(Long.class);
        // 加载释放锁的脚本
        this.unlockScript = new DefaultRedisScript<>();
        this.unlockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("unlock.lua")));
    }

    
    public String tryLock(String lockName, long releaseTime) {
        // 存入的线程信息的前缀,防止与其它JVM中线程信息冲突
        String key = UUID.randomUUID().toString();

        // 执行脚本
        Long result = (Long)redisTemplate.execute(
                lockScript,
                Collections.singletonList(lockName),
                key + Thread.currentThread().getId(), releaseTime);

        // 判断结果
        if(result != null && result.intValue() == 1) {
            return key;
        }else {
            return null;
        }
    }
    
    public void unlock(String lockName, String key) {
        // 执行脚本
        redisTemplate.execute(
                unlockScript,
                Collections.singletonList(lockName),
                key + Thread.currentThread().getId(), null);
    }}

以上是“Redis如何实现可重入锁的设计”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注亿速云行业资讯频道!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Redis如何实现可重入锁的设计

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java可重入锁的实现示例

Java可重入锁的实现示例介绍了ReentraneLock类的实现,包括持有时数、等待队列和锁状态等关键概念。获取锁时,如果锁空闲则获取并增加持有时数,否则加入等待队列。释放锁时,持有时数减1,若变为0则释放锁并唤醒等待线程。可重入性允许同一线程多次获取锁,增加持有时数而无需进入等待队列。示例演示了如何使用ReentraneLock实现可重入锁,多个线程并发执行任务,获取和释放锁多次,体现了可重入性,防止死锁。
Java可重入锁的实现示例
2024-04-02

redis cluster集群模式下实现批量可重入锁

在RedisCluster集群模式下实现批量可重入锁需要解决分布式锁、可重入性、批量操作和解锁四个关键问题。通过使用SETNX命令实现分布式锁,利用MULTI和EXEC命令实现可重入性,借助MGET命令实现批量操作,并使用DEL命令解锁,即可实现批量可重入锁。具体实现步骤涉及定义锁键、开启事务、尝试获取所有锁、提交事务、执行保护代码块和释放所有锁。需要注意在集群模式下MULTI和EXEC命令必须在同一连接中执行,并考虑锁过期时间和使用分布式协调服务来管理锁。
redis cluster集群模式下实现批量可重入锁
2024-04-02

Springboot基于Redisson如何实现Redis分布式可重入锁源码解析

这篇文章主要介绍了Springboot基于Redisson如何实现Redis分布式可重入锁源码解析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、前言我们在实现使用Redi
2023-06-29

Curator实现分布式锁(可重入 不可重入 读写 联锁 信号量 栅栏 计数器)

文章目录 前言代码实践1. 配置2. 可重入锁InterProcessMutex3. 不可重入锁InterProcessSemaphoreMutex4. 可重入读写锁InterProcessReadWriteLock5. 联锁Int
2023-08-19

redis锁是如何实现的

redis锁通过利用redis的setnx和del原子性操作,以及单线程执行特性实现。它通过设置键-值对实现加锁,使用del删除键解锁,并设置过期时间避免死锁。redis锁简单易用、高性能、分布式,但依赖于redis,有单点故障风险,且锁超
redis锁是如何实现的
2024-06-12

java中怎么实现可重入的自旋锁

这篇文章主要介绍了java中怎么实现可重入的自旋锁的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇java中怎么实现可重入的自旋锁文章都会有所收获,下面我们一起来看看吧。说明1、是指试图获得锁的线程不会堵塞,而是
2023-06-30

使用python实现可重入的公平读写锁

在本项目中,读写锁主要应用于多线程服务器场景下的日志文件的读写,以及缓存的获取和更新。 多线程编程的准标准库posix pthread库拥有rwlock, 而python2.7自带的threading库没有读写锁,只有可重入锁RLock,
2023-01-31

如何利用Redis实现分布式锁的高可用

如何利用Redis实现分布式锁的高可用,需要具体代码示例一、引言在分布式系统中,由于多个进程或线程可以同时访问共享资源,会带来资源竞争的问题。为了解决这个问题,需要引入分布式锁来进行资源的互斥访问。Redis作为一种内存数据库,提供了分布式
如何利用Redis实现分布式锁的高可用
2023-11-07

怎么在java中实现内置锁的可重入性

这篇文章给大家介绍怎么在java中实现内置锁的可重入性,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。java基本数据类型有哪些Java的基本数据类型分为:1、整数类型,用来表示整数的数据类型。2、浮点类型,用来表示小数
2023-06-14

详解Java ReentrantLock可重入,可打断,锁超时的实现原理

前面讲解了ReentrantLock加锁和解锁的原理实现,但是没有阐述它的可重入、可打断以及超时获取锁失败的原理,本文就重点讲解这三种情况,需要的可以了解一下
2022-11-13

redis如何实现收藏功能设计

Redis可以通过使用有序集合来实现收藏功能的设计。具体步骤如下:1. 创建两个有序集合,一个用于存储用户收藏的内容,另一个用于存储内容被收藏的次数。假设这两个集合分别为"users:{user_id}:favorites"和"conten
2023-08-31

怎么在java中实现一个可重入的自旋锁

怎么在java中实现一个可重入的自旋锁?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Java可以用来干什么Java主要应用于:1. web开发;2. Android开发;3.
2023-06-14

编程热搜

目录