基于Redisson实现的分布式锁

213 阅读3分钟

在分布式系统架构下,由于并发可能带来的线程操作安全问题。

例如线程1在对某个对象执行业务时,线程2也同时对这个对象进行同样操作,两个线程的同时操做可能会导致业务的重复办理,导致异常。针对与这种问题,可以引入Redisson用来解决这个问题。Redisson 是基于Netty框架,兼容Redis 3.0+ 和 JDK 1.8+,可以简单轻松的整合到SpringBoot项目中。

此次演示的案例是基于 JDK1.8 + SpringBoot 2.6.13 + maven 3.8.2 + redis 5.0.14 + redisson 3.16.0。配置的redis为单机模式,更多配置详细信息,可以点击连接进行查阅 redisson官方文档

1、引入 Redisson 依赖

将以下配置添加到pom文件中,导入redisson依赖包

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

2、创建 Redisson 配置类

演示案例连接的redis是本机启动的服务,所以直接将连接地址写在了创建的过程中。如果启用的是集群模式,可以将具体的配置写在yaml配置文件中。具体的配置方式可以查看官网的配置文档

@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://127.0.0.1:6379");
        return Redisson.create(config);
    }
}

3、创建 Redisson 使用案例

注意: 解锁方法一定要放在finally里面执行,避免因异常出现导致锁无法解除

创建RedissonTestService类,其中有两个方法,分别为concurrentTestredissonLock

  • concurrentTest,用模拟并发操作,创建创建多个线程执行业务方法
  • redissonLock,用于模拟业务操作,在业务操作之前进行加锁,通过睡眠模拟业务执行时间
    • tryLock(),redisson上锁的方法,如果其他线程已经上锁,那么当前线程就无法获取到锁,方法返回结果就为false
    • isHeldByCurrentThread(),用来判断锁是否为当前线程添加,返回boolean类型
    • unlock(),解除锁的方法
@Service
public class RedissonTestService {

    @Resource
    private RedissonClient redissonClient;

    public void concurrentTest() {

        // 创建两个线程模拟并发操作
        Thread thread1 = new Thread(this::redissonLock, "t1");
        Thread thread2 = new Thread(this::redissonLock, "t2");
        thread1.start();
        thread2.start();
    }

    public void redissonLock() {
        RLock lock = redissonClient.getLock("myLock");

        try {
            if (lock.tryLock()) {
                System.out.println(Thread.currentThread().getName() + " - 获取到锁");
                // 睡眠模拟业务操作
                Thread.sleep(5000);
            } else {
                System.out.println(Thread.currentThread().getName() + " - 获取锁失败!");
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock(); // 解锁
                System.out.println(Thread.currentThread().getName() + " - 解除锁");
            }
        }
    }
}

4、创建 Controller 进行测试

创建controller,用于前台访问进行测试的入口,调用service的测试案例

@RestController
public class RedissonTestController {

    @Resource
    private RedissonTestService lockService;
    
    // 浏览器调用模拟操作业务
    @GetMapping("/test-redisson-lock")
    public String testRedissonLock() {
        lockService.concurrentTest();
        return "进行redisson分布式锁测试!";
    }
}

5、测试

测试结果

t1 - 获取锁失败!
t2 - 获取到锁
t2 - 解除锁

t1 - 获取到锁
t2 - 获取锁失败!
t1 - 解除锁

通过观察以上测试输出结果可以方法,当其中一个线程获取到锁之后,另外一个线程无法直接获取到锁。直到锁解除之后,再次调用两个线程再竞争获取锁

总结

在分布式结构的系统下,对业务进行配置锁是十分有必要的,可以有效的保证业务的执行安全,Redisson可以通过简单的配置整合到SpringBoot项目之中,可以轻松实现Redis 的分布式锁机制,确保不会被重复执行或执行出错

以上就是基于Redisson实现的分布式锁的简单演示案例