Redis 实现分布式锁

目录

Redis 实现分布式锁及续约机制详解

1、Redis 分布式锁的基本实现

2、引入守护线程[看门狗机制】


Redis 实现分布式锁及续约机制详解
  • 在分布式系统中,多个节点对同一资源的访问可能会产生竞争,为了解决这个问题,我们需要一种机制来保证同一时间内只有一个节点可以访问资源,分布式锁便是其中的一种解决方案。Redis 作为一种高性能的分布式缓存系统,其自带的 SETNX 命令和过期时间机制非常适合用来实现分布式锁。本文将详细介绍如何利用 Redis 实现分布式锁,并引入一个守护线程为锁自动续约,确保在业务长时间执行的过程中锁不会被意外释放。
1、Redis 分布式锁的基本实现
  • 第一版
  • 在这个简单的实现中,我们通过 setIfAbsent 方法(对应 Redis 的 SETNX 命令)来尝试获取锁。如果获取成功,就执行相应的业务逻辑,处理完后再检查当前锁是否仍属于自己(通过比对 lockId),如果是,则删除锁以释放资源。

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void RedisDistributedLock2() {
        int i = 1;
        // 线程获得唯一标识的锁ID
        String lockId = UUID.randomUUID().toString();
        // 使用 Redis 的 setNx 命令,同时设置过期时间,保证原子性
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("key", lockId, 15, TimeUnit.SECONDS);

        if (Boolean.TRUE.equals(lock)) {
            try {
                i += 10;
                // 处理业务逻辑
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 释放锁
                if (lockId.equals(stringRedisTemplate.opsForValue().get("key"))) {
                    stringRedisTemplate.delete("key");
                }else{
                    System.out.println("当前线程持有锁已过期!");
                }
            }
        }else {
            // 获取锁失败,快速返回
            System.out.println("获取锁失败,当前线程无法执行任务。");
            return; // 直接返回,终止方法执行
        }
    }
 

  •  虽然上述代码能够实现分布式锁的基本功能,但在某些场景下,锁的过期时间可能会过短,导致在业务逻辑尚未执行完毕时锁被自动释放,从而可能导致其他线程错误地获取到锁。为了解决这个问题,我们需要引入一种机制来定期延长锁的过期时间。

2、引入守护线程[看门狗机制】
  • 为了保证在业务逻辑长时间执行时锁不会过期,我们可以通过守护线程来为锁续约。下面是具体的实现

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final int LOCK_EXPIRE_TIME = 15; // 锁的过期时间,单位为秒
    private static final int RENEWAL_INTERVAL = 10; // 续约的间隔时间,单位为秒

    @Test
    void RedisDistributedLock(){
        int i=1;
        //某线程获取到锁唯一标识
        String lockId = UUID.randomUUID().toString();
        //使用Redis的setNx命令,同时过期时间一起,保证原子性
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("key", lockId, LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(lock)){
            // 创建续约标志,确保可以终止续约线程
            AtomicBoolean continueRenewal = new AtomicBoolean(true);

            // 守护线程进行锁续约
            Thread renewalThread = new Thread(() -> {
                try {
                    while (continueRenewal.get()) {
                        Thread.sleep(TimeUnit.SECONDS.toMillis(RENEWAL_INTERVAL));
                        // 如果锁ID匹配,则续约
                        if (lockId.equals(stringRedisTemplate.opsForValue().get("key"))) {
                            stringRedisTemplate.expire("key", LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
                        } else {
                            break; // 锁已不再属于当前线程,退出续约线程
                        }
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });

            renewalThread.setDaemon(true); // 设置为守护线程
            renewalThread.start();


            try{
                i+=10;
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                //如果是当前线程的锁,操作完成释放锁
                if (lockId.equals(stringRedisTemplate.opsForValue().get("key"))){
                    continueRenewal.set(false); // 停止续约线程
                    stringRedisTemplate.delete("key");
                }else{
                    System.out.println("当前线程持有锁已过期!");
                }
            }
        }else {
            // 获取锁失败,快速返回
            System.out.println("获取锁失败,当前线程无法执行任务。");
            return; // 直接返回,终止方法执行
        }
    }
  • 续约线程的引入:在成功获取锁后,我们创建并启动一个守护线程,该线程会每隔一段时间检查当前锁的状态,如果锁仍属于当前线程,则通过 expire 方法延长锁的过期时间。 
  • 续约逻辑:续约线程的执行周期由 RENEWAL_INTERVAL 控制,每次延长的过期时间为 LOCK_EXPIRE_TIME。这样即使业务逻辑执行时间较长,锁也不会因为过期而被释放。
  • 守护线程的终止:在释放锁之前,我们首先将 continueRenewal 置为 false,使守护线程停止续约操作,然后再删除锁。这种设计避免了锁被释放后续约线程仍在运行的风险。

总结:以上便是 Redis 实现分布式锁及其续约机制的详细介绍,希望对你有所帮助。如果你在实际开发中遇到问题或有其他更好的方案,欢迎交流讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值