分布式锁redis
工具类
package com.ruoyi.zlzh.spmms.util;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;/*** Redis分布式锁服务** @author yxy*/
@Component
public class RedisLockService {@Resourceprivate RedisTemplate redisTemplate;private static final String LOCK_PREFIX = "redis_lock:";private static final String UNLOCK_LUA_SCRIPT ="if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) " +"else " +"return 0 " +"end";/*** 尝试获取锁** @param lockKey 锁的key* @param expireTime 锁的过期时间* @param timeUnit 时间单位* @return 锁的值,获取失败返回null*/public String tryLock(String lockKey, long expireTime, TimeUnit timeUnit) {String lockValue = UUID.randomUUID().toString();String key = LOCK_PREFIX + lockKey;Boolean success = redisTemplate.opsForValue().setIfAbsent(key, lockValue, expireTime, timeUnit);return Boolean.TRUE.equals(success) ? lockValue : null;}/*** 释放锁** @param lockKey 锁的key* @param lockValue 锁的值* @return 是否释放成功*/public boolean releaseLock(String lockKey, String lockValue) {String key = LOCK_PREFIX + lockKey;DefaultRedisScript<Long> script = new DefaultRedisScript<>();script.setScriptText(UNLOCK_LUA_SCRIPT);script.setResultType(Long.class);Long result = (Long) redisTemplate.execute(script, Collections.singletonList(key), lockValue);return Long.valueOf(1).equals(result);}/*** 执行带锁的操作** @param lockKey 锁的key* @param expireTime 锁的过期时间* @param timeUnit 时间单位* @param supplier 要执行的操作* @param <T> 返回值类型* @return 操作结果* @throws RuntimeException 获取锁失败时抛出异常*/public <T> T executeWithLock(String lockKey, long expireTime, TimeUnit timeUnit, Supplier<T> supplier) {String lockValue = tryLock(lockKey, expireTime, timeUnit);if (lockValue == null) {throw new RuntimeException("获取分布式锁失败,请稍后重试");}try {return supplier.get();} finally {releaseLock(lockKey, lockValue);}}/*** 执行带锁的操作(默认30秒过期时间)** @param lockKey 锁的key* @param supplier 要执行的操作* @param <T> 返回值类型* @return 操作结果 这个默认30秒 感觉不太好 一般2到3秒*/public <T> T executeWithLock(String lockKey, Supplier<T> supplier) {return executeWithLock(lockKey, 3, TimeUnit.SECONDS, supplier);}
}
使用
@Autowiredprivate RedisLockService redisLockService;@Overridepublic int updateSpMrItem(SpMrItem spMrItem) {// 使用分布式锁保护并发更新String lockKey = "mr_item_update_" + spMrItem.getMrItemId();return redisLockService.executeWithLock(lockKey, () -> {// 获取数据库中的原始记录SpMrItem originalItem = spMrItemMapper.selectSpMrItemByMrItemId(spMrItem.getMrItemId());// 是否唯一if (StringUtils.isNotEmpty(spMrItem.getItemNo())) {if (!spMrItem.getItemNo().equals(originalItem.getItemNo())) {int count = spMrItemMapper.checkItemNoUnique(spMrItem.getItemNo(),spMrItem.getMrId());if (count > 0) {throw new RuntimeException("MR物料条目号已存在,请使用其他");}}}// 检查数量是否发生变化if (originalItem != null && spMrItem.getQty() != null && originalItem.getQty() != null) {// 如果数量发生了变化if (!spMrItem.getQty().equals(originalItem.getQty())) {// 将原来的数量保存到previousQtyspMrItem.setPreviousQty(originalItem.getQty());// 增加变更次数Integer currentChangeCount = originalItem.getChangeCount();if (currentChangeCount == null) {currentChangeCount = 0;}spMrItem.setChangeCount(currentChangeCount + 1);}}spMrItem.setUpdateTime(DateUtils.getNowDate());spMrItem.setUpdateBy(SecurityUtils.getUsername());return spMrItemMapper.updateSpMrItem(spMrItem);});}