基于Redisson使用自定义注解实现分布式锁

基于Redisson使用自定义注解实现分布式锁

1. 实现原理

  1. 基于Redisson;

  2. 实现了一个注解,代码如下:

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DistributedLock {
    
        long TEN_THOUSAND = 10000L;
    
        long EIGHT_THOUSAND = 8000L;
    
        /**
         * 锁的名称
         */
        String name() default "";
    
        /**
         * 锁的过期时间,单位ms,默认8000
         */
        long expireMillis() default EIGHT_THOUSAND;
    
        /**
         * 获取锁的超时时间,单位ms,默认10000
         */
        long acquireTimeoutMillis() default TEN_THOUSAND;
    
    }
    
  3. 实现了一个切面,当一个方法使用了注解@DistributedLock的时候,会在执行方法前,获取分布式锁。代码如下:

    @Aspect
    @Component
    @Slf4j
    public class DistributedLockAspect {
    
        @Resource
        private RedissonClient redissonClient;
    
        @Pointcut("@annotation(com.xxx.xxx.xxx.xxx.DistributedLock)")
        public void myPointcut() {
    
        }
    
        @Around(value = "myPointcut()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            DistributedLock distributedLock = getAnnotation(pjp);
            String lockName = resolveKey(distributedLock.name(), pjp);
            RLock lock = redissonClient.getLock(lockName);
            try {
    
                Stopwatch stopwatch = Stopwatch.createStarted();
                boolean success = lock.tryLock(distributedLock.acquireTimeoutMillis(), distributedLock.expireMillis(),
                    TimeUnit.MILLISECONDS);
                LOGGER.info("tryLock, result:{}, cost:{}", success, stopwatch.stop().elapsed(TimeUnit.MILLISECONDS));
                if (success) {
                    LOGGER.info("获取锁成功,lockName={}", lockName);
                } else {
                    LOGGER.info("获取锁失败,lockName={}", lockName);
                }
                return pjp.proceed();
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                    LOGGER.info("释放锁成功,lockName={}", lockName);
                }
            }
        }
    
        private DistributedLock getAnnotation(ProceedingJoinPoint pjp) {
            MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
            return methodSignature.getMethod().getAnnotation(DistributedLock.class);
        }
    
        private String resolveKey(String key, ProceedingJoinPoint pjp) {
            if (StringUtils.isBlank(key)) {
                return pjp.getSignature().toLongString();
            } else {
                StandardEvaluationContext context = new StandardEvaluationContext();
                Object[] args = pjp.getArgs();
                MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
                String[] params = methodSignature.getParameterNames();
                for (int i = 0; i < params.length; i++) {
                    context.setVariable(params[i], args[i]);
                }
                return new SpelExpressionParser().parseExpression(key).getValue(context, String.class);
            }
        }
    }
    
  4. 本质上是通过lock.tryLock(distributedLock.acquireTimeoutMillis(),distributedLock.expireMillis(),TimeUnit.MILLISECONDS);来获取的分布式锁;

  5. 由于distributedLock.acquireTimeoutMillis()不为0,因此如果一个线程没有拿到锁,会持续等待指定时间,并在期间不断尝试获取锁;目前默认的distributedLock.acquireTimeoutMillis()10s,由于执行业务逻辑一般不需要这么久,因此正常情况下一个线程并不会等待长达10s,主要用于兜底;
    锁的过期时间为distributedLock.expireMillis()目前默认为8s,由于执行业务逻辑一般不需要这么久,因此正常情况下一个线程并不会持有锁长达8s,主要用于兜底;
    使用了SPEL来获取lockName

2. 使用方法

  1. 在任意层的某个方法上,添加@DistributedLock(name = "XXX")即可对"XXX"加锁;

  2. 示例1:

    @DistributedLock(name = "'get'+#codes")
    public void get(String codes) {
        List<String> codeList = Arrays.stream(orgCodes.split(",")).collect(Collectors.toList());
        ext(codeList);
    }
    

    如果上述方法的入参codes111,222,333,444,则经SPEL解析的lockNameget111,222,333,444

  3. 示例2:

    @DistributedLock(name = "'attendance_info'+#attendanceInfo.orderId+#attendanceInfo.date")
    public Integer insertDedup(ConstructionAttendanceInfo attendanceInfo, AtomicInteger dmlType) {
    	// 此处省略
    	return 0;
    }
    

    如果上述方法的入参attendanceInfo中的orderIdaaadate2023-08-01,则经SPEL解析的lockNameattendance_infoaaa2023-08-01

3. 注意事项

使用时,name应指定为name = "'<方法名/唯一标识名>'+#<入参/入参的某个字段>",避免不同的方法生成同样的lockName

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
自定义注解实现分布式锁是一种在分布式系统中解决并发问题的方法。通过在需要加锁的方法上添加自定义注解,可以实现对该方法的并发控制。具体实现步骤如下: 1. 首先,在项目的pom文件中引入相关的依赖,以支持分布式锁的功能。 2. 在项目中创建一个新的目录,并使用注解来定义自定义注解。这个自定义注解可以用来标记需要加锁的方法。 3. 在具体的业务实现类中,使用自定义注解来标记需要加锁的方法。这样,在方法执行时,会根据注解的配置来进行并发控制。 使用自定义注解实现分布式锁的好处是可以避免在每个需要加锁的方法中都引入相关的配置类和方法,提高了代码的可读性和可维护性。此外,还可以将配置和项目打包成jar包,方便在其他项目中引入使用分布式锁相对于普通锁的区别在于,普通锁只能在单体应用中锁住方法,而分布式锁可以在分布式系统中实现并发控制。在分布式系统中,不同实例之间共用代码和数据库,因此需要使用分布式锁来保证并发操作的正确性。分布式锁可以使用一些工具,如Redis,将锁放在共享的资源中,以实现多个实例共用一个锁的效果。 总结来说,自定义注解实现分布式锁是一种方便且可扩展的方式,可以在分布式系统中解决并发问题。 #### 引用[.reference_title] - *1* [自定义注解实现分布式锁](https://blog.csdn.net/qq_37205211/article/details/122140430)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [分布式编程-实现分布式锁-优雅的使用自定义注解实现](https://blog.csdn.net/qq_41692766/article/details/105842467)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [自定义注解,基于redis实现分布式锁](https://blog.csdn.net/weixin_43975276/article/details/131097829)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kuo-Teng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值