【Spring Boot 3】【Redis】分布式锁

【Spring Boot 3】【Redis】分布式锁

背景

软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花费或多或少的时间、检索不止一篇资料才能得出一个可工作的DEMO,这占用了我大量的时间精力。因此本文旨在通过一篇文章即能还原出可工作的、甚至可用于生产的DEMO,期望初学者能尽快地迈过0到1的这一步骤,并在此基础上不断深化对相关知识的理解。

为达以上目的,本文会将开发环境、工程目录结构、开发步骤及源码尽量全面地展现出来,文字描述能简则简,能用代码注释的绝不在正文中再啰嗦一遍,正文仅对必要且关键的信息做重点描述。

介绍

本文介绍Spring Boot + Redis实现分布式锁。

开发环境

分类 名称 版本
操作系统 Windows Windows 11
JDK Oracle JDK 21.0.1
IDE IntelliJ IDEA 2023.2.4
构建工具 Apache Maven 3.9.3
缓存 Redis 7.2

开发步骤及源码

1> 创建Maven工程,添加依赖。

复制代码
    <properties>
        <spring-boot.version>3.2.1</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${spring-boot.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2> 添加应用配置(src/main/resources/application.yml)。

复制代码
spring:
  data:
    redis:
      # 连接地址
      host: 127.0.0.1
      # 端口
      port: 6379
      # Redis数据库索引,默认为 0
      database: 0
      # 用户名(可选)
      # username:
      # 密码(可选)
      # password:
      # 连接超时
      connect-timeout: 5000
      # 读超时
      timeout: 5000
      # Lettuce 客户端配置
      lettuce:
        # 连接池配置
        pool:
          # 最小空闲连接
          min-idle: 0
          # 最大空闲连接
          max-idle: 8
          # 最大活跃连接
          max-active: 8
          # 从连接池获取连接最大超时时间,小于等于 0 则表示不会超时
          max-wait: -1ms

3> 创建Redis配置类,自定义 org.springframework.data.redis.core.RedisTemplate Bean实例。

复制代码
package com.jiyongliang.springboot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        // 设置 key 的序列化方式
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 设置 value 的序列化方式
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        // 设置 hash 中 key 的序列化方式
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        // 设置 hash 中 value 的序列化方式
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        return redisTemplate;
    }
}

4> 创建分布式锁服务类。

复制代码
package com.jiyongliang.springboot.service;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

@RequiredArgsConstructor
@Service
public class DistributedLockService {

    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 获取锁
     *
     * @param key     锁的键
     * @param value   锁的值
     * @param timeout 超时时间
     * @return 是否成功获取锁
     */
    public boolean tryLock(String key, String value, long timeout) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS));
    }

    /**
     * 释放锁
     *
     * @param key   锁的键
     * @param value 锁的值
     * @return 是否成功释放锁
     */
    public boolean unlock(String key, String value) {
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        Object currentValue = ops.get(key);
        if (currentValue != null && Objects.equals(value, currentValue)) {
            Boolean result = redisTemplate.delete(key);
            return Boolean.TRUE.equals(result);
        }
        return false;
    }
}

5> 创建单元测试。

复制代码
package com.jiyongliang.springboot.service;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.TimeUnit;

@SpringBootTest
class DistributedLockServiceTests {

    @Autowired
    DistributedLockService distributedLockService;

    @Test
    void test() throws InterruptedException {
        Assertions.assertThat(distributedLockService.tryLock("lock1", "A", 3)).isTrue();
        Assertions.assertThat(distributedLockService.tryLock("lock1", "A", 3)).isFalse();
        Assertions.assertThat(distributedLockService.unlock("lock1", "A")).isTrue();
        Assertions.assertThat(distributedLockService.tryLock("lock1", "A", 3)).isTrue();
        TimeUnit.SECONDS.sleep(3);
        Assertions.assertThat(distributedLockService.tryLock("lock1", "A", 3)).isTrue();
    }
}

6> 定义SpringBoot应用启动类。

复制代码
package com.jiyongliang.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBoot3RedisDistributedLockApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBoot3RedisDistributedLockApplication.class, args);
    }
}

7> 单元测试结果

工程目录结构

总结

注意一定要添加 jackson-databind 依赖,否则会报错:threw exception with message: com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder

相关推荐
小马爱打代码3 小时前
Redis 集群方案详解:主从复制、哨兵、脑裂、分片集群和哈希槽
数据库·redis·哈希算法
我是一颗柠檬7 小时前
【Redis】发布订阅与消息队列Day8(2026年)
数据库·redis·后端·缓存
爱吃羊的老虎7 小时前
【JAVA】python转java:Spring Boot 入门
java·spring boot·python
sukioe7 小时前
Redis 持久化+高可用详解:RDB/AOF/混合/主从/哨兵/集群
数据库·redis·缓存
_qingche8 小时前
H2 数据库到 MySQL 数据迁移
java·数据库·spring boot·mysql·spring·重构·kotlin
XWalnut10 小时前
Redis从入门到精通
数据库·redis·缓存
码语智行11 小时前
系统启动时初始化数据功能分析
java·spring boot
invicinble11 小时前
推荐一下,遇到的几本比较好的书
spring boot
憧憬成为java架构高手的小白12 小时前
git工作流程简化版
java·spring boot·git
YDS82913 小时前
DeepSeek RAG&MCP + Agent智能体项目 —— 动态决策策略的接口对接
java·spring boot·ai·agent·spring ai·deepseek