redis分布式锁-Redisson(RedissonClient)

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的实现方式。

https://www.javadoc.io/doc/org.redisson/redisson/3.11.6/org/redisson/api/RLockAsync.html

https://www.cnblogs.com/cjsblog/p/9831423.html

https://blog.csdn.net/clypm/article/details/80598074

 

 

### Java Redis 分布式锁使用 Redisson 实现的方法 #### 创建 Maven 或 Gradle 依赖项 为了在项目中集成 Redisson,需先引入相应的依赖库。对于Maven构建工具而言,在`pom.xml`文件里添加如下配置: ```xml <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.17.6</version> </dependency> ``` 而对于Gradle来说,则是在`build.gradle`文件中增加下面的内容: ```groovy implementation 'org.redisson:redisson:3.17.6' ``` #### 初始化 Redisson 客户端实例 创建一个名为 `RedissonClient` 的单例来管理连接池和其他资源。 ```java import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; public class RedissonUtil { private static final String REDIS_HOST = "localhost"; private static final int REDIS_PORT = 6379; private static volatile RedissonClient redissonClient; public static synchronized RedissonClient getRedissonClient(){ if (null == redissonClient){ Config config = new Config(); config.useSingleServer().setAddress("redis://" + REDIS_HOST + ":" + REDIS_PORT); redissonClient = Redisson.create(config); } return redissonClient; } } ``` 这段代码定义了一个静态方法用于返回唯一的 `RedissonClient` 对象,并确保只初始化一次[^1]。 #### 获取分布式锁并执行业务逻辑 当需要对某些共享资源实施互斥访问控制时,可以通过调用 `RLock lock = redisson.getLock(lockName)` 来获得一把基于给定名称的分布式可重入锁。一旦获取到了这把锁就可以安全地进行后续操作了。 ```java import org.redisson.api.RLock; import java.util.concurrent.TimeUnit; // ...其他导入语句... public void processWithDistributedLock(String resourceName) throws InterruptedException { RLock resourceLock = RedissonUtil.getRedissonClient().getLock(resourceName); try{ boolean isLocked = resourceLock.tryLock(5, 10, TimeUnit.SECONDS); // 尝试等待最多五秒去得到锁,设置十秒钟自动解锁超时 if(isLocked){ System.out.println(Thread.currentThread().getName()+"获得了锁"); // 执行具体的业务逻辑... Thread.sleep(2000L); // 假设这里是要做的工作 System.out.println(Thread.currentThread().getName()+"完成了任务"); }else{ throw new RuntimeException("未能取得锁定!"); } }finally{ if(resourceLock.isHeldByCurrentThread()){ resourceLock.unlock(); // 解除当前持有的锁 System.out.println(Thread.currentThread().getName()+"已解除锁"); } } } ``` 上述例子演示了如何利用 Redisson 库提供的 API 接口完成基本功能——尝试占有指定名字的空间范围内的独占权限;如果成功则继续下一步骤直到结束之前都要记得释放掉所占用的权利以便让别的进程有机会参与竞争[^4]。 #### 关闭客户端连接 应用程序关闭前应当妥善清理所有打开过的网络链接以及分配出去的对象实例。 ```java Runtime.getRuntime().addShutdownHook(new Thread(() -> { if(null != RedissonUtil.redissonClient && !RedissonUtil.redissonClient.isShuttingDown()){ RedissonUtil.redissonClient.shutdown(); } })); ``` 这样可以在JVM退出的时候优雅地断开与Redis服务器之间的通信链路。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值