你要的是 “同一业务类型(bussType)串行执行” 的效果 —— 即同一个 bussType 只能被一个线程处理,上一个线程执行完(释放锁),下一个线程才能进入。这种场景用 Redisson 的分布式锁 最合适,它支持细粒度锁(按 bussType 分锁),还能避免死锁、自动续期,比原生 Redis 锁更稳定。
以下是 基于 Redisson 的 “单 bussType 串行执行” 完整实现,直接替换原代码即可,兼顾并发安全和易用性:
核心思路
锁粒度:按 bussType 生成独立锁(比如 code:lock:order、code:lock:user),不同业务类型互不影响(并行执行),同一业务类型串行执行。
锁特性:用 Redisson 的 RLock,支持自动重试、超时释放、自动续期(避免长事务导致锁过期)。
执行逻辑:线程先拿对应 bussType 的锁 → 拿到锁后执行 “查 - 算 - 更” → 执行完释放锁 → 下一个线程竞争锁。
步骤 1:引入 Redisson 依赖(以 Spring Boot 为例)
xml
<!-- Redisson 核心依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.3</version> <!-- 推荐用稳定版,和 Spring Boot 版本兼容 -->
</dependency>
步骤 2:配置 Redisson(application.yml)
yaml
spring:
redis:
host: 127.0.0.1 # 你的 Redis 地址
port: 6379
password: # Redis 密码(无则留空)
database: 0
redisson:
singleServerConfig:
address: redis://${spring.redis.host}:${spring.redis.port}
password: ${spring.redis.password}
database: ${spring.redis.database}
connectionMinimumIdleSize: 10 # 最小空闲连接数
connectionPoolSize: 64 # 连接池大小
idleConnectionTimeout: 10000 # 空闲连接超时时间
connectTimeout: 10000 # 连接超时时间
timeout: 3000 # 命令执行超时时间
步骤 3:改造 Service 方法(核心代码)
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.TimeUnit;
@Service
public class SysCodeServiceImpl extends ServiceImpl<SysCodeConfMapper, SysCodeConfEntity> implements SysCodeService {
@Autowired
private RedissonClient redissonClient;
// 锁前缀:避免和其他 Redis 锁冲突
private static final String LOCK_PREFIX = "sys_code:lock:";
// 锁等待时间:5秒(线程最多等5秒拿锁,超时返回失败)
private static final long LOCK_WAIT_TIME = 5;
// 锁自动释放时间:30秒(防止线程异常导致锁不释放)
private static final long LOCK_LEASE_TIME = 30;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW) // 独立事务,减少锁持有时间
public String getCodeByType(String bussType) {
// 1. 生成当前业务类型的专属锁(细粒度锁,不影响其他 bussType)
RLock lock = redissonClient.getLock(LOCK_PREFIX + bussType);
try {
// 2. 尝试获取锁:最多等5秒,拿到锁后30秒自动释放(支持自动续期)
boolean isLocked = lock.tryLock(LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS);
if (!isLocked) {
log.error("获取编码锁失败,业务类型:{}(当前并发过高,请稍后重试)", bussType);
throw new RuntimeException("编码生成繁忙,请稍后重试");
}
// 3. 拿到锁后,执行核心逻辑(此时同一 bussType 只有当前线程在执行)
SysCodeConfEntity sysCodeConfEntity = baseMapper.selectOne(
Wrappers.lambdaQuery(SysCodeConfEntity.class).eq(SysCodeConfEntity::getBussType, bussType)
);
if (sysCodeConfEntity == null) {
log.warn("业务类型{}未配置编码规则", bussType);
return null;
}
// 4. 生成新编码 + 更新数据库(串行执行,无并发冲突)
String newCode = CodeUtil.generateNewCode(sysCodeConfEntity);
baseMapper.updateById(sysCodeConfEntity);
return newCode;
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复线程中断状态
log.error("编码生成被中断,业务类型:{}", bussType, e);
return null;
} finally {
// 5. 释放锁(必须在 finally 中执行,确保锁一定释放)
if (lock.isHeldByCurrentThread()) { // 防止未拿到锁却尝试释放
lock.unlock();
}
}
}
}
关键特性说明(为什么用 Redisson 而不是原生 Redis 锁?)
自动续期:如果事务执行时间超过 LOCK_LEASE_TIME(30 秒),Redisson 会自动给锁续期(默认每 10 秒续一次),避免锁过期导致并发问题。
非阻塞获取锁:tryLock 方法会在指定时间内尝试获取锁,超时返回失败,不会导致线程一直阻塞。
细粒度锁:每个 bussType 对应一个独立的锁,比如 code:lock:order 和 code:lock:user 是两个不同的锁,不同业务类型可以并行执行,不会全局串行(提升性能)。
死锁防护:锁有超时时间,即使线程异常崩溃,Redis 也会在超时后自动释放锁,避免死锁。
922

被折叠的 条评论
为什么被折叠?



