分布式锁模板工具类
# 1. 添加RedissonConfig配置
@Configuration
public class RedissonConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;@Value("${spring.redis.password:}")private String password;@Value("${spring.redis.database:}")private int database;/*** 单机模式配置*/@Beanpublic RedissonClient redissonClient() {Config config = new Config();config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password.isEmpty() ? null : password).setDatabase(database);return Redisson.create(config);}
# 2. 分布式锁执行配置类(用于控制加锁行为)
私有无参构造,通过工厂获取配置,避免主动set参数时丢失核心参数
/*** 分布式锁执行配置类,用于控制加锁行为*/
@Data
@Accessors(chain = true)
public class LockExecutionConfig {private LockExecutionConfig(){}/*** 锁的key*/private String lockKey = "lockKey";/*** 加锁失败后的业务错误信息提示*/private String errorMsg = "try lock failed";/*** 锁的过期时间, 默认5秒*/private long expire = 5000;/*** 获取锁的超时时间, 默认1秒*/private long timeout = 1000;/*** 时间单位, 默认毫秒*/private TimeUnit timeUnit = TimeUnit.MILLISECONDS;/*** 最大重试次数, 默认不重试, 建议不超过5次*/private int maxRetries = 0;/*** 重试间隔时间(单位:毫秒),默认 1 秒*/private long retryInterval = 1000;static LockExecutionConfig getLockExecutionConfig(){return new LockExecutionConfig();}}
# 3. 配置工厂类
需要保证工厂类和配置类位于同一包下, 通过LockExecutionConfig.getLockExecutionConfig()获取配置类对象
public interface LockExecutionConfigFactory {/*** 获取锁执行配置* @param lockKey 锁的key* @param errorMsg 加锁失败后的业务错误信息提示* @param expire 锁的过期时间* @param timeout 获取锁的超时时间* @param timeUnit 时间单位* @param maxRetries 最大重试次数* @param retryInterval 重试间隔时间* @return*/static LockExecutionConfig getLockExecutionConfig(String lockKey,String errorMsg,long expire,long timeout,TimeUnit timeUnit,int maxRetries,long retryInterval){LockExecutionConfig lockExecutionConfig = LockExecutionConfig.getLockExecutionConfig();lockExecutionConfig.setLockKey(lockKey).setErrorMsg(errorMsg).setExpire(expire).setTimeout(timeout).setTimeUnit(timeUnit).setMaxRetries(maxRetries).setRetryInterval(retryInterval);return lockExecutionConfig;}
}
# 4. 具体业务配置类生成(建议)
/*** 实销单锁前缀常量类*/
public interface ActualSoLockExecutionConfig {/*** 实销单锁前缀*/public static final String ACTUAL_SO_LOCK_PREFIX = "sales.actualSo.dml:";/*** 实销单上传锁*/public static final String ACTUAL_SO_LOCK_UPLOAD = "sales.actualSo:upload";/*** 实销单获取锁超时时间*/public static final long ACTUAL_SO_LOCK_TIMEOUT = 1000L;/*** 实销单dml锁有效期*/public static final long ACTUAL_SO_LOCK_EXPIRE = 5000L;/*** 实销单上传锁有效期*/public static final long ACTUAL_SO_LOCK_UPLOAD_EXPIRE = 10000L;/*** 获取实销单dml操作锁配置** @return*/public static LockExecutionConfig getActualSoDmlLockConfig(String lockKey, String errorMsg) {return LockExecutionConfigFactory.getLockExecutionConfig(lockKey, errorMsg, ACTUAL_SO_LOCK_EXPIRE, ACTUAL_SO_LOCK_TIMEOUT, TimeUnit.MILLISECONDS, 0,1000L);}/*** 获取实销单上传操作锁配置** @return*/public static LockExecutionConfig getActualSoUploadLockConfig(String lockKey, String errorMsg) {return LockExecutionConfigFactory.getLockExecutionConfig(lockKey, errorMsg, ACTUAL_SO_LOCK_UPLOAD_EXPIRE, ACTUAL_SO_LOCK_TIMEOUT, TimeUnit.MILLISECONDS, 0,1000L);}}
# 5. 分布式锁模板工具
/*** 锁模板工具类*/
@Slf4j
@Component
public class LockTemplateUtils {public static final String ERROR_MSG = "Failed to acquire lock";@Autowiredprivate RedissonClient redissonClient;/*** 在获取锁后执行指定的业务逻辑,并返回结果** @param lockKey 锁的key* @param errorMsg 获取锁失败时抛出的异常信息* @param expire 锁过期时间 单位:毫秒* @param waitTime 获取锁超时时间 单位:毫秒* @param supplier 需要执行的业务逻辑(返回结果)* @param <T> 返回值类型* @return 执行结果*/public <T> T executeWithLock(String lockKey, String errorMsg, long expire, long waitTime, Supplier<T> supplier) {if (supplier == null) {throw new IllegalArgumentException("Supplier is null");}if (redissonClient == null) {log.error("redissonClient is not initialized");throw new IllegalStateException("redissonClient is not initialized");}RLock lock = redissonClient.getLock(lockKey);boolean isLocked = false;try {// 尝试加锁isLocked = lock.tryLock(waitTime, expire, TimeUnit.MILLISECONDS);if (isLocked) {// 执行业务逻辑return supplier.get();} else {// 获取锁失败log.error("Failed to acquire lock for key: {}", lockKey);throw new LockException(StringUtils.isNotBlank(errorMsg) ? errorMsg : ERROR_MSG);}} catch (InterruptedException e) {log.error("Failed to acquire lock for key: {}, reason: interrupted: {}", lockKey, e.getMessage(), e);Thread.currentThread().interrupt();throw new LockException(StringUtils.isNotBlank(errorMsg) ? errorMsg : ERROR_MSG);} finally {// 释放锁if (isLocked) {lock.unlock();}}}/*** 在获取锁后执行指定的业务逻辑,并返回结果(带重试机制)** @param config 锁执行配置参数* @param supplier 需要执行的业务逻辑(返回结果)* @param <T> 返回值类型* @return 执行结果*/public <T> T executeWithLock(LockExecutionConfig config, Supplier<T> supplier) {if (config == null) {throw new IllegalArgumentException("LockExecutionConfig is null");}if (supplier == null) {throw new IllegalArgumentException("Supplier is null");}if (redissonClient == null) {throw new IllegalStateException("RedissonClient is not initialized");}RLock lock = redissonClient.getLock(config.getLockKey());final TimeUnit timeUnit = config.getTimeUnit();for (int attempt = 0; attempt <= config.getMaxRetries(); attempt++) {try {if (lock.tryLock(config.getTimeout(), config.getExpire(), timeUnit)) {try {return supplier.get();} finally {try {lock.unlock();} catch (Exception e) {log.error("Failed to release lock. Lock key: {}", config.getLockKey(), e);}}} else {if (attempt < config.getMaxRetries()) {long backOffTime = (1L << attempt) * config.getRetryInterval();Thread.sleep(backOffTime);} else {log.warn("Failed to acquire lock after {} attempts. Lock key: {}", attempt, config.getLockKey());throw new LockException(StringUtils.isNotBlank(config.getErrorMsg()) ? config.getErrorMsg() : ERROR_MSG);}}} catch (InterruptedException e) {Thread.currentThread().interrupt();log.error("Interrupted while trying to acquire lock. Lock key: {}", config.getLockKey(), e);throw new LockException(StringUtils.isNotBlank(config.getErrorMsg()) ? config.getErrorMsg() : ERROR_MSG);}}// 正常流程不会走到这里,仅为了编译通过return null;}}
## 推荐使用方法二
public <T> T executeWithLock(LockExecutionConfig config, Supplier<T> supplier){}