JAVA中阶培训(一)-策略模式与Redis拓展

2021-05-11  本文已影响0人  一击必中

一、前情提要

本次培训是在SpringBoot标准化培训的基础上,进行的相关拓展。未参加SpringBoot标准化培训的请自行翻阅之前文档,培训的目的是帮助java开发成员“厌倦”了乏味的增删改查的工作后,渴望对更加复杂或优美的java技术进一步获知,进而提升自己的java水平、拓展自己的知识面。

以下所有功能示例,均来自生产项目

二、功能实例

1、java策略模式

概念:定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。

策略模式把对象本身和运算规则区分开来,因此我们整个模式也分为三个部分。
环境类(Context):用来操作策略的上下文环境,也就是我们游客。
抽象策略类(Strategy):策略的抽象,出行方式的抽象
具体策略类(ConcreteStrategy):具体的策略实现,每一种出行方式的具体实现。

实际例子(物联网智能设备处理)

项目结构
image.png
1、自定义注释@interface

参考链接:自定义注释@interface的用法理解_zhangbeizhen18的博客-CSDN博客

/**
 * describe:事件处理策略
 *
 * @author tangn
 * @date 2019/04/24
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TuyaMessageEvent {
    /**
     * 事件类型
     *
     * @return 事件类型
     */
    String value();
}
2、策略转发接口
/**
 * @author tangn
 * @desc 处理转发接口
 * @date 2019/04/24
 */
public interface EventNotifyStrategy {

    /**
     * 处理前准备通知参数
     *
     * @param pulsarMessage 通知消息
     * @return 处理结果
     */
    boolean preEventHandle(PulsarMessage pulsarMessage);

    /**
     * 处理后处理通知结果
     *
     * @param pulsarMessage 通知消息
     * @throws Exception 异常
     */
    void afterEventHandle(PulsarMessage pulsarMessage) throws Exception;
    
}
3、事件处理策略判断类

/**
 * @author tangn
 * @desc 事件处理策略判断类
 * @date 2019/04/24
 */
public class EventNotifyStrategyFactory {

    /**
     * 私有化构造函数,单例开始
     */
    private EventNotifyStrategyFactory() {

    }

    private static class Builder {
        private static final EventNotifyStrategyFactory EVENT_NOTIFY_STRATEGY_FACTORY = new EventNotifyStrategyFactory();
    }

    @SuppressWarnings("unused")
    public static EventNotifyStrategyFactory getInstance() {
        return Builder.EVENT_NOTIFY_STRATEGY_FACTORY;
    }

    /**
     * 单例结束
     */
    private static final String PAY_STRATEGY_IMPLEMENTATION_PACKAGE = "com.decentchina.cronjob.pulsar.strategy.event";
    private static final Map<String, Class> STRATEGY_MAP = new HashMap<>();

    // 获取所有事件类型策略
    static {
        Reflections reflections = new Reflections(PAY_STRATEGY_IMPLEMENTATION_PACKAGE);
        Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(TuyaMessageEvent.class);
        classSet.forEach(aClass -> {
            TuyaMessageEvent payAnnotation = aClass.getAnnotation(TuyaMessageEvent.class);
            STRATEGY_MAP.put(payAnnotation.value(), aClass);
        });
    }

    /**
     * 根据支付策略类型获取支付策略bean
     *
     * @param type class
     * @return 实体
     */
    public static EventNotifyStrategy getStrategy(String type) {
        // 反射获取支付策略实现类clazz
        Class clazz = STRATEGY_MAP.get(type);
        if (StringUtils.isEmpty(clazz)) {
            return null;
        }
        // 通过applicationContext获取bean
        return (EventNotifyStrategy) BeansUtil.getBean(clazz);
    }
}
4、提供事件处理服务的抽象类
/**
 * @author tangn
 * @desc 提供事件处理服务的抽象类
 * @date 2019/04/24
 */
@Slf4j
@Service
public abstract class AbstractEventNotifyService implements EventNotifyStrategy {

    protected String eventType;

    /**
     * 无需处理的事件
     */
    private static final String NONE_EVENT = "none";

    /**
     * 各类事件处理
     *
     * @param eventType     事件类型
     * @param pulsarMessage 通知消息
     * @throws Exception 异常
     * @desc 涂鸦pulsar的消费订阅模式是失效备源模式,消息只会在某一个客户端被消费,消费如果失败,就会被堆积,失效时间为2小时。为防止
     * 异常模式下大量消息堆积导致异常或者断连,所有消费消息都必须被正确消费,无用消息走NONE_EVENT时间,所有异常模式不能使用return
     */
    public void eventHandle(String eventType, PulsarMessage pulsarMessage) throws Exception {
        this.eventType = eventType;
        boolean checkResult = preEventHandle(pulsarMessage);
        if (checkResult) {
            log.info("[设备状态变更事件]成功 eventType[{}]", eventType);
        } else {
            log.info("[设备状态变更事件]失败 eventType[{}]", eventType);
            this.eventType = NONE_EVENT;
            pulsarMessage.setEvent(NONE_EVENT);
        }
        afterEventHandle(pulsarMessage);
        log.info("[设备状态变更事件][{}]事件类型通知信息[{}] 处理完成", pulsarMessage.getEvent(), pulsarMessage);
    }
}

5、事件通知实现类
/**
 * @author tangn
 * @desc: 事件通知实现类
 * @date 2019/04/24
 */
@Slf4j
@Service
public class EventNotifyServiceImpl extends AbstractEventNotifyService {

    @Override
    public boolean preEventHandle(PulsarMessage pulsarMessage) {
        EventNotifyStrategy eventNotifyStrategy = EventNotifyStrategyFactory.getStrategy(this.eventType);
        if (eventNotifyStrategy == null) {
            log.info("没有[{}]类型的事件\r\n", this.eventType);
            return false;
        }
        return eventNotifyStrategy.preEventHandle(pulsarMessage);
    }

    @Override
    @SuppressWarnings("ConstantConditions")
    public void afterEventHandle(PulsarMessage pushMessage) throws Exception {
        EventNotifyStrategy payStrategy = EventNotifyStrategyFactory.getStrategy(this.eventType);
        payStrategy.afterEventHandle(pushMessage);
    }
}
6、具体策略功能实现

/**
 * @author tangn
 * @date 2021/2/19 10:07
 * 设备在线
 */
@Slf4j
@Service
@TuyaMessageEvent("online")
public class OnlineEvent implements EventNotifyStrategy {
    @Resource
    private StoreDeviceDao storeDeviceDao;
    @Resource
    private DeviceDao deviceDao;
    @Resource
    private OffOnlineAlarmService offOnlineAlarmService;

    /**
     * 预处理时间
     *
     * @param pulsarMessage 通知消息
     * @return boolean
     */
    @Override
    public boolean preEventHandle(PulsarMessage pulsarMessage) {
        return true;
    }

    @Override
    public void afterEventHandle(PulsarMessage pulsarMessage) throws Exception {
        // 获取设备信息
        StoreDevices storeDevice = storeDeviceDao.queryStoreDeviceInfoByDeviceUid(pulsarMessage.getDevId());
        if (Objects.isNull(storeDevice)) {
            log.warn("设备在线,查询不到该设备[{}]", pulsarMessage.getDevId());
        } else {
            // 检测设备是否在线
            if (CommonStatusEnum.OFF.equals(storeDevice.getOnline())) {
                // 更新在线状态
                deviceDao.updateDeviceOnlineState(storeDevice.getId(), CommonStatusEnum.ON);
                //上线提醒
                offOnlineAlarmService.onlineAlarm(storeDevice.getStoreCode(), pulsarMessage.getDevId(), LocalDateTime.now());
            }
        }
    }
}
7、策略调用
   abstractEventNotifyService.eventHandle("online", pulsarMessage);

2、Redis巧妙使用,别光会用string(会员系统抢券)

/**
 * @author zhongzq
 * @date 2019-12-18 10:37
 */
@Service
public class CouponCenterServiceImpl implements CouponCenterService {
    @Resource
    private CouponCenterDao couponCenterDao;
    @Resource
    private UserService userService;
    @Resource(name = "redisTemplateObject")
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private OwnCouponDao ownCouponDao;

    /**
     * 领券中心领券
     *
     * @param user             会员
     * @param shopCouponCenter 领券中心券
     * @return : com.orangeconvenient.common.entity.MessageBean
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public MessageBean<ShopCouponCenterVO> receive(User user, ShopCouponCenterVO shopCouponCenter) {
        if (!ShopCouponCenterActivityStatusEnum.PROCESSING.equals(shopCouponCenter.getCenterCouponStatus())) {
            return new MessageBean<>(ErrorCodeEnum.NO, "此优惠券已抢光");
        }
        LocalDateTime endTime = null;
        if (ShopCouponCenterTypeEnum.TIME_LIMIT.equals(shopCouponCenter.getCenterType())) {
            ShopCouponCenterActivity shopCouponCenterActivity = Optional.ofNullable(couponCenterDao.getActivityById(shopCouponCenter.getActivityId()))
                    .orElseThrow(() -> new ErrorCodeException(ErrorCodeEnum.NONE, "该活动已结束"));
            if (ShopCouponCenterActivityStatusEnum.OVER.equals(shopCouponCenterActivity.getActivityStatus()) ||
                    !LocalDateTime.now().isBefore(shopCouponCenterActivity.getEndTime())) {
                return new MessageBean<>(ErrorCodeEnum.NONE, "该活动已结束");
            }
            endTime = shopCouponCenterActivity.getEndTime();
        }
        boolean isTimeRange = CouponEffectiveTypeEnum.DATE_TYPE_FIX_TIME_RANGE.equals(shopCouponCenter.getTimeType());
        LocalDateTime couponBeginTime = isTimeRange ?
                shopCouponCenter.getBeginTime() : LocalDateTimeUtil.begin(LocalDate.now().plusDays(shopCouponCenter.getFixedBeginTerm()));
        LocalDateTime couponEndTime = isTimeRange ?
                shopCouponCenter.getEndTime() : couponBeginTime.toLocalDate().plusDays(shopCouponCenter.getFixedBeginTerm() > 0 ? shopCouponCenter.getFixedTerm() - 1 : shopCouponCenter.getFixedTerm()).atTime(23, 59, 59);
        if (isTimeRange && !LocalDateTime.now().isBefore(couponEndTime)) {
            return new MessageBean<>(ErrorCodeEnum.NO, "此优惠券已抢光");
        }
        RedisAtomicLong surplusQuantity = getSurplusQuantity(shopCouponCenter, endTime);
        if (surplusQuantity.get() <= 0L) {
            return new MessageBean<>(ErrorCodeEnum.NO, "此优惠券已抢光");
        }
        String totalNumRedisKey = Constant.ORANGE_VIP_COUPON_CENTER_RECEIVE_PREFIX + shopCouponCenter.getId();
        if (Objects.nonNull(shopCouponCenter.getPerTotalLimit())) {
            if (!Objects.equals(Boolean.TRUE, redisTemplate.opsForHash().hasKey(totalNumRedisKey, user.getId())) &&
                    redisTemplate.opsForHash().putIfAbsent(totalNumRedisKey, user.getId(), couponCenterDao.getUserTotalNum(user.getId().longValue(), shopCouponCenter.getId())) &&
                    ShopCouponCenterTypeEnum.TIME_LIMIT.equals(shopCouponCenter.getCenterType())) {
                redisTemplate.expire(totalNumRedisKey, LocalDateTimeUtil.goneSeconds(LocalDateTime.now(), endTime), TimeUnit.SECONDS);
            }
            if ((int) redisTemplate.opsForHash().get(totalNumRedisKey, user.getId()) >= shopCouponCenter.getPerTotalLimit()) {
                return new MessageBean<>(ErrorCodeEnum.NO, "您已达到领取次数,无法领取");
            }
        }
        doSendCouponCenter(user, shopCouponCenter, couponBeginTime, couponEndTime, surplusQuantity, endTime, totalNumRedisKey);
        return new MessageBean<>(ErrorCodeEnum.OK, "领取成功");
    }

    /**
     * 发放领券中心优惠券
     *
     * @param user             会员
     * @param shopCouponCenter 领券中心优惠券
     * @param couponBeginTime  优惠券有效期开始时间
     * @param couponEndTime    优惠券有效期结束时间
     * @param surplusQuantity  redis剩余数量
     * @param endTime          限时抢券结束时间
     * @param totalNumRedisKey 总领取数量redisKey
     */
    private void doSendCouponCenter(User user, ShopCouponCenterVO shopCouponCenter, LocalDateTime couponBeginTime, LocalDateTime couponEndTime, RedisAtomicLong surplusQuantity, LocalDateTime endTime, String totalNumRedisKey) {
        try {
            long surplusNum = surplusQuantity.decrementAndGet();
            if (surplusNum < 0L) {
                throw new ErrorCodeException(ErrorCodeEnum.NO, "此优惠券已抢光");
            }
            if (!Objects.equals(Boolean.TRUE, redisTemplate.opsForHash().hasKey(totalNumRedisKey, user.getId())) &&
                    redisTemplate.opsForHash().putIfAbsent(totalNumRedisKey, user.getId(), couponCenterDao.getUserTotalNum(user.getId().longValue(), shopCouponCenter.getId())) &&
                    ShopCouponCenterTypeEnum.TIME_LIMIT.equals(shopCouponCenter.getCenterType())) {
                redisTemplate.expire(totalNumRedisKey, LocalDateTimeUtil.goneSeconds(LocalDateTime.now(), endTime), TimeUnit.SECONDS);
            }
            if ((int) redisTemplate.opsForHash().get(totalNumRedisKey, user.getId()) >= shopCouponCenter.getPerTotalLimit()) {
                throw new ErrorCodeException(ErrorCodeEnum.NO, "您已达到领取次数,无法领取");
            }
            Long increment = redisTemplate.opsForHash().increment(totalNumRedisKey, user.getId(), 1L);
            if (increment > shopCouponCenter.getPerTotalLimit()) {
                redisTemplate.opsForHash().increment(totalNumRedisKey, user.getId(), -1L);
                throw new ErrorCodeException(ErrorCodeEnum.NO, "您已达到领取次数,无法领取");
            }
            List<UserOwnCoupon> userOwnCoupons = Collections.singletonList(UserOwnCoupon.builder().ownCouponId(shopCouponCenter.getCouponId()).userId(user.getId().longValue()).tradeNo(null).useStatus(CouponUseStatusEnum.NO_USED)
                    .couponNo(CheckUtil.fillZero(shopCouponCenter.getCouponId(), 5) + CheckUtil.fillZero(user.getId().longValue(), 5) + System.nanoTime())
                    .startValidateTime(couponBeginTime).endValidateTime(couponEndTime)
                    .couponSource(OwnCouponSourceEnum.COUPON_CENTER).couponSourceId(shopCouponCenter.getId()).build());
            try {
                ownCouponDao.insertUserOwnCoupons(userOwnCoupons.size(), userOwnCoupons);
                // 领券达到总数量,关闭券
                if (couponCenterDao.ifCenterCouponNumMax(shopCouponCenter) >= shopCouponCenter.getPreIssueQuantity()
                        &&
                        couponCenterDao.close(shopCouponCenter, ShopCouponCenterActivityStatusEnum.OVER) == 1) {
                    redisTemplate.delete(Arrays.asList(Constant.ORANGE_VIP_COUPON_CENTER_PREFIX + shopCouponCenter.getId(), totalNumRedisKey));
                }
            } catch (Exception e) {
                if (Objects.nonNull(shopCouponCenter.getPerTotalLimit())) {
                    redisTemplate.opsForHash().increment(totalNumRedisKey, user.getId(), -1L);
                }
                throw e;
            }
        } catch (Exception e) {
            surplusQuantity.incrementAndGet();
            throw e;
        }
    }

    /**
     * 获取redis中的领券中心优惠券剩余数量
     *
     * @param shopCouponCenter 领券中心优惠券
     * @param endTime          限时抢券结束时间
     * @return : org.springframework.data.redis.support.atomic.RedisAtomicLong
     */
    @SuppressWarnings("ConstantConditions")
    private RedisAtomicLong getSurplusQuantity(ShopCouponCenter shopCouponCenter, LocalDateTime endTime) {
        RedisAtomicLong surplusQuantity;
        String surplusQuantityRedisKey = Constant.ORANGE_VIP_COUPON_CENTER_PREFIX + shopCouponCenter.getId();
        if (!Objects.equals(Boolean.TRUE, redisTemplate.hasKey(surplusQuantityRedisKey))) {
            surplusQuantity = new RedisAtomicLong(surplusQuantityRedisKey, redisTemplate.getConnectionFactory());
            if (surplusQuantity.compareAndSet(0, shopCouponCenter.getPreIssueQuantity() - couponCenterDao.getNowTotalNum(shopCouponCenter.getId())) &&
                    ShopCouponCenterTypeEnum.TIME_LIMIT.equals(shopCouponCenter.getCenterType())) {
                surplusQuantity.expireAt(LocalDateTimeUtil.localDateTime2Date(endTime));
            }
        } else {
            surplusQuantity = new RedisAtomicLong(surplusQuantityRedisKey, redisTemplate.getConnectionFactory());
        }
        return surplusQuantity;
    }
}

讲解点:


1、RedisAtomicLong
知识延伸1:
(使用RedisAtomicLong优化性能_饭团小哥哥iop的博客-CSDN博客_redisatomiclong
知识延伸2:
AtomicLong用法_weixin_39967234的博客-CSDN博客

// 取值
 surplusQuantity = new RedisAtomicLong(surplusQuantityRedisKey, redisTemplate.getConnectionFactory());
// 加值
 long surplusNum = surplusQuantity.incrementAndGet();
// 减值
 long surplusNum = surplusQuantity.decrementAndGet();
// 更值
public final boolean compareAndSet(int expect, int update);
// 解释(凭自身能力理解)
value的值为expect的值,第二是把value的值更新为
update,这两步是原子操作,在没有多线程锁的情况下,借助cpu锁保证数据安全。
原因:在RedisAtomicLong内部有一个 private volatile int value; 
volatile保证变量的线程间可见性,compareAndSet方法实际上是做了两部操作,第一是比较

2.redis过期时间使用

// RedisAutomicLong 过期
 surplusQuantity.expireAt(LocalDateTimeUtil.localDateTime2Date(endTime));
// hash过期
 redisTemplate.expire(totalNumRedisKey, LocalDateTimeUtil.goneSeconds(LocalDateTime.now(), endTime), TimeUnit.SECONDS);

3.redisTemplate.opsForHash()
redisTemplate.opsForHash()_小泽-CSDN博客

// 放值
redisTemplate.opsForHash().putIfAbsent(totalNumRedisKey, user.getId(), couponCenterDao.getUserTotalNum(user.getId().longValue(), shopCouponCenter.getId()))
// 取值
redisTemplate.opsForHash().get(totalNumRedisKey, user.getId())
// 增值
Long increment = redisTemplate.opsForHash().increment(totalNumRedisKey, user.getId(), 1L);
// 减值
redisTemplate.opsForHash().increment(totalNumRedisKey, user.getId(), -1L);

4.redisTemplate.delete

// 删除多个key
redisTemplate.delete(Arrays.asList(Constant.ORANGE_VIP_COUPON_CENTER_PREFIX + shopCouponCenter.getId(), totalNumRedisKey));

3、Redis分布式锁(千云系统防重点击)

  /**
 * 防重点击限制
 *
 * @author 韩涛
 * @date 2020年03月28日 10时33分
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatClickLimit {

    /**
     * 操作超时时间
     *
     * @return 超时时间
     */
    long operateTimeOut() default 5;

    /**
     * 操作超时时间单位
     *
     * @return 时间单位
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;

}
/**
 * 防重点击切面
 *
 * @author 韩涛
 * @date 2019年12月3日14:20:07
 */
@Slf4j
@Aspect
@Component
public class RepeatClickLimitAspect {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 防重点击RedisKey前缀
     */
    private static final String REPEAT_CLICK_LIMIT_REDIS_KEY_PREFIX = "FasRepeatClickLimit_";

    /**
     * 防重点击实现方法
     *
     * @param point            连接点
     * @param repeatClickLimit 防重点击注解
     * @return 方法执行结果
     * @throws Throwable 方法执行异常
     */
    @Around("@annotation(repeatClickLimit)")
    public Object doDistributedLock(ProceedingJoinPoint point, RepeatClickLimit repeatClickLimit) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String storeCodeLimit = request.getHeader("storeCodeLimit");
        // 获取执行方法的类名
        String className = point.getTarget().getClass().getName();
        // 获取执行方法的方法名
        String methodName = point.getSignature().getName();
        // RedisKey=前缀+类名+方法名+管理员ID
        String redisKey = REPEAT_CLICK_LIMIT_REDIS_KEY_PREFIX + className + methodName + storeCodeLimit;
        // 使用Redis分布式锁,超时时间为注解自定义时间
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.multi();
        redisTemplate.opsForValue().setIfAbsent(redisKey, "");
        redisTemplate.expire(redisKey, repeatClickLimit.operateTimeOut(), repeatClickLimit.timeUnit());
        List<Object> result = redisTemplate.exec();
        log.info("请求:[{}]", !(Boolean) result.get(0));
        if (!(Boolean) result.get(0)) {
            log.info("返回");
            // 获取签名
            Signature signature = point.getSignature();
            // 若不是方法签名直接抛异常
            if (!(signature instanceof MethodSignature)) {
                throw new ErrorCodeException(ErrorCodeEnum.ERROR, "操作频繁,请稍后重试");
            }
            MethodSignature methodSignature = (MethodSignature) signature;
            // 根据方法名及参数类型获取方法
            Method currentMethod = point.getTarget().getClass().getMethod(methodSignature.getName(),
                    methodSignature.getParameterTypes());
            // 获取返回值类型
            Class<?> returnType = currentMethod.getReturnType();
            // 返回值类型为SimpleMessage或MessageBean时,直接返回,其他抛出异常
            if (SimpleMessage.class.equals(returnType)) {
                return new SimpleMessage(ErrorCodeEnum.NO, "操作频繁,请稍后重试");
            }
            if (MessageBean.class.equals(returnType)) {
                return MessageBean.builder().errorCode(ErrorCodeEnum.OK.getCode()).errorMsg("操作频繁,请稍后重试").build();
            }
            throw new ErrorCodeException(ErrorCodeEnum.ERROR, "操作频繁,请稍后重试");
        }
        try {
            log.info("方法执行");
            //执行目标方法
            return point.proceed();
        } finally {
            log.info("删除");
            // 删除RedisKey
            redisTemplate.delete(redisKey);
        }
    }

}

分布式锁文件

/**
 * REDIS锁
 *
 * @author 陈豆豆
 * @date 2020-03-16
 */
@Slf4j
@Component
public class RedisLockService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /***
     * 加锁
     * @param key
     * @param value 当前时间+超时时间(超时时间最好设置在10秒以上,保证在不同的项目获取到的时间误差在控制范围内)
     * @return 锁住返回true
     */
    public boolean lock(String key, String value) {
        try {
            //setNX 返回boolean
            Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(key, value);
            if (aBoolean) {
                return true;
            }
            //如果锁超时 ***
            String currentValue = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
                //获取上一个锁的时间
                String oldvalue = redisTemplate.opsForValue().getAndSet(key, value);
                if (!StringUtils.isEmpty(oldvalue) && oldvalue.equals(currentValue)) {
                    return true;
                }
            }
        } catch (Exception e) {
            log.error("加锁发生异常[{}]", e.getLocalizedMessage(), e);
        }
        return false;
    }

    /***
     * 解锁
     * @param key
     * @param value
     * @return
     */
    public void unlock(String key, String value) {
        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            if (StringUtils.isBlank(currentValue)) {
                return;
            }
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
                redisTemplate.delete(key);
            }
        } catch (Exception e) {
            log.error("解锁异常[{}]", e.getLocalizedMessage(), e);
        }
    }
}

使用方法

/**
 * 门店现金收银对账
 *
 * @author guojw
 * @date 2020/09/24
 */
@Slf4j
@AllArgsConstructor
public class CashReconciliationThread implements Runnable {
    private CashReconciliationService cashReconciliationService;
    private RedisLockService redisLockService;

    @Override
    public void run() {
        long currentTime = Instant.now().plus(15, MINUTES).toEpochMilli();
        boolean lock = redisLockService.lock(TaskNameEnum.CASH_PAYMENT.getStr(), String.valueOf(currentTime));
        //防止重复开启
        if (!lock) {
            return;
        }
        try {
            cashReconciliationService.cashPaymentReconciliation();
        } catch (Exception e) {
            log.error("现金收银对账线程异常[{}]", e.getLocalizedMessage(), e);
        } finally {
            redisLockService.unlock(TaskNameEnum.CASH_PAYMENT.getStr(), String.valueOf(currentTime));
        }
    }
}

知识延伸

超卖了100瓶飞天茅台!!酿成一个重大事故!Redis分布式锁使用不当 (qq.com)

上一篇下一篇

猜你喜欢

热点阅读