一、为什么要用Redisson 分布式锁?

1.1. 🧠 Redisson 锁的核心优势
优势描述
自动续期默认锁时间到期前自动延长,避免业务未完成锁就被释放
高性能依赖 Redis 的高性能通信,适合高并发环境
多种锁类型满足不同业务需求(读写、信号量等)
容易集成支持 Spring Boot,配置简单
跨语言Redis 是跨平台的,Redisson 作为客户端只需接入即可
1.2. 🔁 常见使用场景
  • 防止重复提交订单

  • 秒杀库存扣减

  • 用户余额扣减

  • 定时任务分布式执行

  • 防止缓存击穿(缓存预热互斥)

二、如果使用?

✅ 目标场景:

模拟 多个线程同时对一个共享变量 count 自增 的业务场景,类似于:

  • 多用户同时抢购/下单

  • 并发写入数据库(如库存扣减、积分增加)

  • 并发写入 Redis 缓存值

  • 并发调用某个接口,可能造成数据覆盖或计数错误

2.1 引入redisson依赖
      <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.23.5</version>
        </dependency>
2.2 配置文件application.yml
spring:
  application:
      name: spring-boot-demo
  data:
    redis:
      # 数据库
      database: 0
      # 主机
      host: localhost
      # 端口
      port: 6379
      # 密码
      # password:
      # 读超时
      timeout: 5s
      # 连接超时
      connect-timeout: 5s
2.3 测试demo 有锁
    @Autowired
    RedissonClient redissonClient;
    
     @Test
    void lockTest() throws InterruptedException {
        // 1.创建一个倒计时器,用于主线程等待 1000 个子线程执行完毕后再打印 count
        for (int i = 0; i < 10; i++) {
            count = 0;
            CountDownLatch countDownLatch = new CountDownLatch(10000);
            for (int j = 0; j < 10000; j++) {
                new Thread(() -> {
                    // 基于redis 实现redisson 分布式锁对象
                    RLock lock = redissonClient.getLock("lock-redisson");
                    try {
                        //上锁
                        lock.lock();
                        //执行业务逻辑(+1)
                        count++;
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        //释放锁
                        lock.unlock();
                    }
                    //每调用一次,计数器减 1,当 1000 个线程都执行完后,计数器变为 0。
                    countDownLatch.countDown();
                }).start();
            }
            countDownLatch.await();
            System.out.println("有锁情况下 count = " + count);
        }

    }

 打印结果,重复测试几遍都是准的 

2.4 测试无锁的demo
    /**
     * 不加锁测试
     */
    @Test
    void noLockTest() throws InterruptedException {

        for (int i = 0; i < 10; i++) {
            count = 0;
            CountDownLatch latch = new CountDownLatch(10000);
            for (int j = 0; j < 10000; j++) {
                new Thread(() -> {
                    count++;
                    latch.countDown();
                }).start();
            }
            latch.await();
            System.out.println("第 " + i + " 次执行,count = " + count);
        }

    }

重复测试几轮 会有一些情况打印出的数据比实际的10000少一点

三、为什么会出现不加锁的情况下优势数据比理想数据要少一点情况出现呢?

这是因为 count++ 不是原子操作,它实际是下面三个步骤的组合:

1. 从主内存读取 count 到 CPU 寄存器
2. 寄存器中的值 +1
3. 把新值写回主内存

多个线程并发时,可能出现下面这种 “线程覆盖” 的情形:

  1. 线程A读取 count = 99

  2. 线程B也读取 count = 99

  3. 线程A计算完写入 100

  4. 线程B也写入 100 → A 的结果被覆盖了!

最终两个线程都增加了,但结果只 +1,这就是数据丢失现象,是典型的线程安全问题。

四、为什么加了 Redisson 分布式锁就不会错?

RLock.lock() 实际底层是 Redis 的 SETNX + 过期时间 实现分布式互斥锁。即:

  • 线程 A 获取锁后,线程 B 会阻塞等待(或失败),不会同时执行临界区 count++

  • 保证 同一时间只有一个线程修改 count

  • 所以结果就总是准确的:10000

更多推荐