redis分布式锁详解Redisson(RedissonClient)

 更新时间:2025年11月19日 09:10:28   作者:码上走人  
Redisson提供了多种锁和实用方法,实现了对数据的增删改查等操作,RedissonClient接口的实现类中,重点介绍了重入锁、公平锁和联锁的实现方式,在实际应用中,设置定时过期的分布式锁需要考虑服务宕机或重启的问题,可以通过记录锁的Set来解决

RedissonClient中提供了好多种锁,还有其它很多实用的方法。

Redisson是Redis官方推荐的Java版的Redis客户端。实现了对数据的增删改查等操作。

Redisson实现了RedissonClient的接口。这里只介绍其中的锁。

依赖

<dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson</artifactId>
      <version>3.10.7</version>
</dependency>

重入锁 RedissonLock

重入锁可以通过Redisson的getLock方法获取

@Override
public RLock getLock(String name) {
    return new RedissonLock(connectionManager.getCommandExecutor(), name);
}
/**
     * 获取锁-同一个线程可重入
     * @param lockKey  锁的名称
     * @param waitTime 获取锁的等待时间
     * @param leaseTime 锁的持续时间
     * @param unit  时间的单位
     * @return 获取锁的结果
     */
    public Boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 1. 最常见的使用方法
            //lock.lock();
            // 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
            //lock.lock(10, TimeUnit.SECONDS);
            boolean locked = lock.tryLock(waitTime, leaseTime, unit);
            if (locked) lockKeys.add(lockKey);
            return locked;
        } catch (InterruptedException e) {
            System.out.println(String.format("尝试获取锁%s失败", lockKey));
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }

    /**
     * 解锁 - 重入的方式,所以同一个线程加了几次锁就要释放几次锁
     * @param lockKey 锁的值
     */
    public boolean unLock(String lockKey) {
        try {
            RLock lock = redissonClient.getLock(lockKey);
            if (null != lock && lock.isHeldByCurrentThread()) { //判断锁是否存在,和是否当前线程加的锁。
                lock.unlock();
                return lockKeys.remove(lockKey);
            }
        } catch (Exception e) {
            System.out.println(String.format("解锁锁%s失败", lockKey));
            e.printStackTrace();
        }
        return false;
    }

重入锁的异步执行方式

/**
     * 异步获取锁-同一个线程可重入
     * @param lockKey  锁的名称
     * @param waitTime 获取锁的等待时间
     * @param leaseTime 锁的持续时间
     * @param unit  时间的单位
     * @return 获取锁的结果
     */
    public Boolean tryLockAsync(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 1. 最常见的使用方法
            //lock.lockAsync();
            // 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
            //lock.lockAsync(10, TimeUnit.SECONDS);
            RFuture<Boolean> locked = lock.tryLockAsync(waitTime, leaseTime, unit);
            if (locked.get()) lockKeys.add(lockKey);
            return locked.get();
        } catch (InterruptedException | ExecutionException e) {
            System.out.println(String.format("尝试获取锁%s失败", lockKey));
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }

    /**
     * 解锁 - 重入的方式,所以同一个线程加了几次锁就要释放几次锁
     * @param lockKey 锁的值
     */
    public boolean unAsyncLock(String lockKey) {
        try {
            RLock lock = redissonClient.getLock(lockKey);
            if (null != lock && lock.isHeldByCurrentThread()) { //判断锁是否存在,和是否当前线程加的锁。
                RFuture<Void> future = lock.unlockAsync();
                if(future.await(5 * 1000) && future.isSuccess()) {
                    return lockKeys.remove(lockKey);
                }
            }
        } catch (Exception e) {
            System.out.println(String.format("解锁%s失败", lockKey));
            e.printStackTrace();
        }
        return false;
    }

公平锁

改公平锁是可重入的,在提供了自动过期解锁功能的同时,保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程。同时也提供了异步的方式。

实现方式参照上一个锁的实现。

@Override
public RLock getFairLock(String name) {
    return new RedissonFairLock(connectionManager.getCommandExecutor(), name);
}
/**
     * 公平锁
     *
     * @param lockKey 锁的名称
     * @param waitTime 获取锁的等待时间
     * @param leaseTime 锁的持续时间
     * @param unit 时间的单位
     *
     * @return 获取锁的结果
     */
    public Boolean tryFairLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getFairLock(lockKey);
        try {
            // 1. 最常见的使用方法
            //lock.tryLock();
            // 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
            //lock.tryLock(10, TimeUnit.SECONDS);
            boolean locked = lock.tryLock(waitTime, leaseTime, unit);
            if (locked)
                lockKeys.add(lockKey);
            return locked;
            /* 异步实现方式
            lock.lockAsync();
            lock.lockAsync(10, TimeUnit.SECONDS);
            RFuture<Boolean> locked = lock.tryLockAsync(waitTime, leaseTime, unit);
            if (locked.get()) lockKeys.add(lockKey);
            return locked.get();*/
        } catch (InterruptedException e) {
            System.out.println(String.format("尝试获取锁%s失败", lockKey));
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }

    /**
     * 解锁 - 重入的方式,所以同一个线程加了几次锁就要释放几次锁
     *
     * @param lockKey 锁的值
     */
    public boolean unFairLock(String lockKey) {
        try {
            RLock lock = redissonClient.getFairLock(lockKey);
            if (null != lock && lock.isHeldByCurrentThread()) { //判断锁是否存在,和是否当前线程加的锁。
                lock.unlock();
                return lockKeys.remove(lockKey);

                //异步方式删除锁
                /*RFuture<Void> future = lock.unlockAsync();
                if (future.await(5 * 1000) && future.isSuccess()) {
                    return lockKeys.remove(lockKey);
                }*/
            }
        } catch (Exception e) {
            System.out.println(String.format("解锁%s失败", lockKey));
            e.printStackTrace();
        }
        return false;
    }

联锁(MultiLock)

Redisson的RedissonMultiLock对象可以将多个RLock对象关联为一个联锁,每个RLock对象实例可以来自于不同的Redisson实例。

需要注意的是,在锁的释放时,可以单独释放Redisson添加的锁,其他锁不会释放依旧存在。

/**
     * 连锁-只有所有的RedissonClient都锁成功才算成功
     *
     * @param waitTime 获取锁的等待时间
     * @param leaseTime 锁的持续时间
     * @param unit 时间的单位
     *
     * @return 获取锁的结果
     */
    public Boolean tryMultiLock(RedissonClient redisson1,RedissonClient redisson2, long waitTime, long leaseTime, TimeUnit unit){
        RLock lock1 = redisson1.getLock("zhong:test:lock1");
        RLock lock2 = redisson2.getLock("zhong:test:lock2");
        RLock lock = redissonClient.getMultiLock(lock1, lock2);
        try {
            // 同时加锁:lock1 lock2 lock3, 所有的锁都上锁成功才算成功。
            lock.lock();
            // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
            boolean res = lock.tryLock(waitTime, unit);
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return false;
    }
    /**
     * 连锁 - 需要遵循谁加的锁设计释放锁,可以单独释放自己加的锁
     *
     */
    public boolean unMultiLock(RedissonClient client ,RedissonClient client1) {
        try {
            List<RLock> locks = new ArrayList<>();
            locks.add(client.getLock("zhong:test:lock1"));
            locks.add(client1.getLock("zhong:test:lock2"));
            RedissonMultiLock lock = new RedissonMultiLock(locks.toArray(new RLock[0]));
            lock.unlock();

            //异步方式删除锁
            /*RFuture<Void> future = lock.unlockAsync();
            if (future.await(5 * 1000) && future.isSuccess()) {
                return lockKeys.remove(lockKey);
            }*/
        } catch (Exception e) {
            System.out.println(String.format("解锁失败"));
            e.printStackTrace();
            return false;
        }
        return true;
    }

 RedissonClient还提供了红锁,读写锁等。

在实际应用中我们最常用的分布式锁一般都是设置定时过期的。

这样的锁在实际应用中存在一个问题就是服务宕机或重启这个锁在redis上是一直存在的。一旦重启就可能会导致所有线程无法获取到锁。

解决办法就是在加锁的时候将锁记录到Set里面。释放锁的时候将记录Set中的锁删除,在服务停止之前就就根据set记录里面的锁先将欧锁释放。

这样就能保证重启后能获取到锁。实现方式参考DisposableBean的实现方式。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • MybatisPlus多条件 or()的使用问题小结

    MybatisPlus多条件 or()的使用问题小结

    这篇文章主要介绍了MybatisPlus多条件 or()的使用问题小结,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-05-05
  • Java 中比较两个 long 类型变量大小的方法(实例详解)

    Java 中比较两个 long 类型变量大小的方法(实例详解)

    比较两个long类型变量的大小时,由于是基本数据类型,直接使用Java 内置的关系运算符即可,这些运算符比较的是变量的实际值,而非内存地址,下面给大家介绍Java中比较两个long类型变量大小的方法,感兴趣的朋友一起看看吧
    2025-06-06
  • Java Process类的详解及实例代码

    Java Process类的详解及实例代码

    这篇文章主要介绍了Java Process类的详解及实例代码的相关资料,需要的朋友可以参考下
    2017-02-02
  • SpringBoot自动装配Import示例详解

    SpringBoot自动装配Import示例详解

    SpringBoot中@Import注解的使用可以帮助开发者将指定的Bean或配置类导入到IOC容器中,该注解支持四种用法:导入Bean、导入配置类、实现ImportSelector接口和实现,感兴趣的朋友一起看看吧
    2024-09-09
  • Java聊天室之实现接收和发送Socket

    Java聊天室之实现接收和发送Socket

    这篇文章主要为大家详细介绍了Java简易聊天室之实现接收和发送Socket功能,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以了解一下
    2022-10-10
  • 基于CyclicBarrier和CountDownLatch的使用区别说明

    基于CyclicBarrier和CountDownLatch的使用区别说明

    这篇文章主要介绍了基于CyclicBarrier和CountDownLatch的使用区别说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • (starters)springboot-starter整合阿里云datahub方式

    (starters)springboot-starter整合阿里云datahub方式

    这篇文章主要介绍了(starters)springboot-starter整合阿里云datahub方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • Java线程并发访问代码分析

    Java线程并发访问代码分析

    这篇文章主要介绍了Java线程并发访问代码分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • Java操作数据库(行级锁,for update)

    Java操作数据库(行级锁,for update)

    这篇文章主要介绍了Java操作数据库(行级锁,for update),文章围绕Java操作数据库的相关资料展开详细内容,需要的小伙伴可以参考一下,希望对你有所帮助
    2021-12-12
  • 使用@Value为静态变量导入并使用导入的静态变量进行初始化方式

    使用@Value为静态变量导入并使用导入的静态变量进行初始化方式

    这篇文章主要介绍了使用@Value为静态变量导入并使用导入的静态变量进行初始化方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02

最新评论