自定义分布式锁注解

学习基础:

定义加锁接口

public interface DistributedLock extends Lock {

    /**
     * 尝试获取分布式锁
     *
     * @param lockKey       锁
     * @param requestId     请求标识
     * @param expireSeconds 锁有效时间
     * @param overTime      超时时间
     * @param timeUnit      时间单位
     * @return 是否获取成功
     * @throws InterruptedException 中断异常
     */
    boolean tryLock(String lockKey, String requestId, int expireSeconds, int overTime, TimeUnit timeUnit) throws InterruptedException;


    /**
     * 尝试获取分布式分段锁
     * @param lockKey
     * @param segments 分段标识
     * @param requestId
     * @param expireSeconds
     * @param overTime
     * @param timeUnit
     * @return 返回成功加锁的段
     * @throws InterruptedException
     */
    String tryLock(String lockKey, List<String> segments , String requestId, int expireSeconds, int overTime, TimeUnit timeUnit) throws InterruptedException ;


        /**
         * 释放分布式锁
         *
         * @param lockKey   锁
         * @param requestId 请求标识
         */
    boolean unlock(String lockKey, String requestId);
}

实现加锁的方法

加锁本质是为了实现互斥,只让一个访问资源。redis分布式锁是根据lockKey这个key去查是否有对应的value,如果值不存在,说明并没有人访问资源,则加锁成功。如果值存在,说明已经有人在访问资源,则加锁失败。

@Component
public class RedisDistributedLock implements DistributedLock {

    /**
     * 成功
     */
    private final static Long SUCCESS = 1L;
    /**等待200ms*/
    private final static Long WAIT_TIME = 200L;


    /**分段锁间的时间间隔*/
    private final static Long SEGMENT_WAIT_TIME = 5L;


    private final static Long WAIT_TIME_1 = 50L;


    /**
     * 缓存
     */
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void lock() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        throw new InterruptedException();
    }

    @Override
    public boolean tryLock() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean tryLock(String lockKey, String requestId, int expireSeconds, int overTime, TimeUnit timeUnit) throws InterruptedException {
        long lastTime = System.nanoTime();
        // 获取锁的超时时间
        long timeout = timeUnit.toNanos(overTime);
        for (; ; ) {
            boolean isGet = tryGetDistributedLock(lockKey, requestId, expireSeconds, timeUnit);

            // 获取成功
            if (isGet) {
                return true;
            }
            long now = System.nanoTime();

            // 超时
            if (timeout <= 0) {
                throw new DefinitelyRuntimeException("当前活动火热,稍后再试...");
            }
            //等待WAIT_TIME
            Thread.sleep(WAIT_TIME);

            timeout -= now - lastTime;
            lastTime = now;

            // 线程已被销毁
            if (Thread.interrupted()) {
                throw new InterruptedException();
            }
        }
    }


    public String tryLock(String lockKey, List<String> segments , String requestId, int expireSeconds, int overTime, TimeUnit timeUnit) throws InterruptedException {

        long lastTime = System.nanoTime();
        // 获取锁的超时时间
        long timeout = timeUnit.toNanos(overTime);
        for (; ; ) {
            for (int i = 0; i < segments.size(); i++) {
                boolean sucess = tryGetDistributedLock(lockKey + segments.get(i), requestId, expireSeconds, timeUnit);
                if (sucess) {
                    return  segments.get(i);
                }
                long now = System.nanoTime();
                // 超时
                if (timeout <= 0) {
                    throw new DefinitelyRuntimeException("加锁超时");
                }
                //等待WAIT_TIME
                Thread.sleep(SEGMENT_WAIT_TIME);

                timeout -= now - lastTime;
                lastTime = now;

                // 线程已被销毁
                if (Thread.interrupted()) {
                    throw new InterruptedException();
                }
            }
            //等待WAIT_TIME
            Thread.sleep(WAIT_TIME_1);
        }
    }




    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean unlock(String lockKey, String requestId) {
        return releaseDistributedLock(lockKey, requestId);
    }

    @Override
    public void unlock() {
        throw new UnsupportedOperationException();
    }

    @SuppressWarnings("NullableProblems")
    @Override
    public Condition newCondition() {
        throw new UnsupportedOperationException();
    }

    /**
     * 尝试获取分布式锁
     *
     * @param lockKey       锁
     * @param requestId     请求标识
     * @param expireSeconds 缓存有效时间
     * @param timeUnit      时间单位
     * @return 是否获取成功
     */
    private boolean tryGetDistributedLock(String lockKey, String requestId, int expireSeconds, TimeUnit timeUnit) {
        // 设置锁,不存在时才能设置成功,表示被锁住
        if (redisTemplate == null) {
            return false;
        }
        Boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireSeconds, timeUnit);

        if (isLock != null && isLock) {
            return true;
        }
        return false;
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey   锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    private boolean releaseDistributedLock(String lockKey, String requestId) {
        // 用脚本来保证同步操作
        String script =
                "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Long result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId);
        return SUCCESS.equals(result);
    }
}

自定义一个加锁注解

/**
 * 分布式锁注解
 *
 */
@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dl {

    /**
     * 锁定的缓存key名称,若为空,则为类完整名 + 方法名 + 后缀
     *
     * @return 锁定的缓存key名称
     */
    String value() default "";

    /**
     * 缓存key后缀参数所在位置,与{@link #spEl()}属性冲突,优先级低于{@link #spEl()}
     *
     * @return 位置
     */
    int suffixKeyIndex() default -1;

    /**
     * 同步锁定的时间,单位秒,默认60
     *
     * @return 同步锁定的时间
     */
    int lockTimeSeconds() default 60;

    /**
     * 获取锁的超时时间
     *
     * @return 获取锁的超时时间
     */
    int overTime() default 60;

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

    /**
     * 缓存key后缀的spEl表达式,与{@link #suffixKeyIndex()}属性冲突,优先级高于{@link #suffixKeyIndex()}
     *
     * @return 缓存key后缀的spEl表达式
     */
    String spEl() default "";
}

增加切面,指向自定义的锁注解

当每次使用Dl注解的时候,都会调用下面的distributedLock()方法,即添加了注解的方法都会加分布式锁。

@Aspect
@Component
@Order(-1)
@ConditionalOnBean(DistributedLock.class)
public class DistributedAspect {

    /**
     * 分布式锁
     */
    @Autowired
    private DistributedLock distributedLock;

    /**
     * @param proceedingJoinPoint proceedingJoinPoint
     */
    @Around("@annotation(com.nascent.ecrp.mall.springframework.distribute.Dl)")
    public Object distributedLock(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String requestId = UUID.randomUUID().toString();
        MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = methodSignature.getMethod();
        Dl distributedKey = method.getAnnotation(Dl.class);
        String lockKey = distributedKey.value();

        if (StringUtils.isBlank(lockKey)) {
            // 若默认的key为空,则生成一个
            lockKey = method.getDeclaringClass().getName() + ":" + method.getName();
        }
        // 解析缓存key的后缀
        int suffixKeyIndex = distributedKey.suffixKeyIndex();
        String suffixKey = "";
        String spEl = distributedKey.spEl();

        if (StringUtils.isNotBlank(spEl)) {
            suffixKey = ":" + SpElUtils
                .getValue(spEl, proceedingJoinPoint.getArgs(), methodSignature.getParameterNames());

        } else if (suffixKeyIndex > -1) {
            Object[] args = proceedingJoinPoint.getArgs();
            suffixKey += ":" + args[suffixKeyIndex];
        }
        lockKey += suffixKey;
        lockKey = "distributed_lock:" + lockKey;
        try {
            if (distributedLock.tryLock(lockKey, requestId, distributedKey.lockTimeSeconds(), distributedKey.overTime(), distributedKey.timeUnit())) {
                return proceedingJoinPoint.proceed();

            } else {
                throw new DefinitelyRuntimeException("系统挤爆了,请稍后再试");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new DefinitelyRuntimeException(e);

        } finally {
            distributedLock.unlock(lockKey, requestId);
        }
    }
}

方法中如何使用自定义的注解

 @Override
    @Dl(value = "submit", spEl = "#customer.openId")
    @Transactional
    public BeanCommonResult cancelLike(FreeTrialReportLikeMobileRequest request, Customer customer) {

        WmActivityFreeTrialLogDO freeTrialLog = WmActivityFreeTrialLogDao.dao().findLogByParticipateId(request.getTrialLogGuid());
        if (freeTrialLog == null) {
            throw new BaseRuntimeException("参数有误");
        }

        WmActivityFreeTrialReportLikeLog log = trialReportLikeLogMapper.selectByUserActLog(request.getTrialLogGuid(), customer.getOpenId());

        Date now = new Date();
        if (log == null) {
            throw new BaseRuntimeException("点赞记录不存在");
        } else {
            if (Constants.Globals.GLOBALS_NO.equals(log.getState())) {
                throw new BaseRuntimeException("请勿重复提交");
            }
        }
        log.setState(0);
        log.setUpdateTime(now);
        trialReportLikeLogMapper.updateByPrimaryKeySelective(log);


        //点赞数量 自减
        WmActivityFreeTrialLogDao.dao().incrLikeCount(freeTrialLog.getGuid(), -1);

        return new BeanCommonResult();
    }

参考文章
SpringBoot中使用Aspect实现切面,超详细
@Aspect 注解使用详解
如何实现自定义注解

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Redis自定义分布式锁,你可以使用注解来简化锁的使用。下面是一个示例: 首先,你可以创建一个自定义注解,例如`@DistributedLock`。这个注解可以用来修饰需要加锁的方法。 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DistributedLock { String value() default ""; // 锁的key,默认为空 long expire() default 30000; // 锁的过期时间,默认为30秒 } ``` 接下来,你可以创建一个切面类,用于处理`@DistributedLock`注解。在该切面类中,使用Redis来实现分布式锁的功能。 ```java @Aspect @Component public class DistributedLockAspect { private final RedisTemplate<String, String> redisTemplate; public DistributedLockAspect(RedisTemplate<String, String> redisTemplate) { this.redisTemplate = redisTemplate; } @Around("@annotation(distributedLock)") public Object distributedLock(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable { String lockKey = distributedLock.value(); // 获取锁的key long expire = distributedLock.expire(); // 获取锁的过期时间 // 加锁操作 Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", expire, TimeUnit.MILLISECONDS); if (locked != null && locked) { try { // 执行方法体 return joinPoint.proceed(); } finally { // 释放锁 redisTemplate.delete(lockKey); } } else { throw new RuntimeException("Failed to acquire distributed lock."); } } } ``` 在上述代码中,我们使用`@Around`注解将切面绑定到所有使用`@DistributedLock`注解的方法上。在切面方法内部,首先获取锁的key和过期时间,然后尝试加锁。如果成功获取到锁,则执行方法体,并在方法执行完毕后释放锁。如果获取锁失败,则抛出异常。 最后,你可以在需要加锁的方法上使用`@DistributedLock`注解。 ```java @Service public class ExampleService { @Autowired private RedisTemplate<String, String> redisTemplate; @DistributedLock(value = "myLockKey", expire = 5000) public void exampleMethod() { // 需要加锁的方法体 } } ``` 上述示例中,`exampleMethod()`方法使用了`@DistributedLock`注解,并指定了锁的key为"myLockKey",过期时间为5秒。 这样,你就可以通过注解方式来简化Redis自定义分布式锁的使用了。请注意,在实际使用中,你需要配置好Redis连接和注入RedisTemplate实例。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值