使用自定义注解实现redisson分布式锁

 更新时间:2022年02月17日 11:31:28   作者:SillinessPlus  
这篇文章主要介绍了使用自定义注解实现redisson分布式锁,具有很好的参考价值,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

自定义注解实现redisson分布式锁

自定义注解

package com.example.demo.annotation;
import java.lang.annotation.*;
/**
 * desc: 自定义 redisson 分布式锁注解
 *
 * @author: 邢阳
 * @mail: xydeveloper@126.com
 * @create 2021-05-28 16:50
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Lock {
    /**
     * 锁的key spel 表达式
     *
     * @return
     */
    String key();
    /**
     * 持锁时间
     *
     * @return
     */
    long keepMills() default 20;
    /**
     * 没有获取到锁时,等待时间
     *
     * @return
     */
    long maxSleepMills() default 30;
}

aop解析注解

package com.example.demo.utils;
import com.example.demo.annotation.Lock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
 * desc: 解析 自定义 redisson 分布式锁注解
 *
 * @author: 邢阳
 * @mail: xydeveloper@126.com
 * @create 2021-05-28 16:50
 */
@Aspect
@Component
public class LockAspect {
    @Autowired
    private RedissonClient redissonClient;
    /**
     * 用于SpEL表达式解析.
     */
    private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
    /**
     * 用于获取方法参数定义名字.
     */
    private final DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    @Around("@annotation(com.example.demo.annotation.Lock)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object object = null;
        RLock lock = null;
        try {
            // 获取注解实体信息
            Lock lockEntity = (((MethodSignature) proceedingJoinPoint.getSignature()).getMethod())
                    .getAnnotation(Lock.class);
            // 根据名字获取锁实例
            lock = redissonClient.getLock(getKeyBySpeL(lockEntity.key(), proceedingJoinPoint));
            if (Objects.nonNull(lock)) {
                if (lock.tryLock(lockEntity.maxSleepMills(), lockEntity.keepMills(), TimeUnit.SECONDS)) {
                    object = proceedingJoinPoint.proceed();
                } else {
                    throw new RuntimeException();
                }
            }
        } finally {
            if (Objects.nonNull(lock) && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return object;
    }
    /**
     * 获取缓存的key
     * 
     * key 定义在注解上,支持SPEL表达式
     *
     * @return
     */
    public String getKeyBySpeL(String spel, ProceedingJoinPoint proceedingJoinPoint) {
        MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
        String[] paramNames = defaultParameterNameDiscoverer.getParameterNames(methodSignature.getMethod());
        EvaluationContext context = new StandardEvaluationContext();
        Object[] args = proceedingJoinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        return String.valueOf(spelExpressionParser.parseExpression(spel).getValue(context));
    }
}

service中使用注解加锁使用

/**
 * desc: 锁
 *
 * @author: 邢阳
 * @mail: xydeveloper@126.com
 * @create 2021-05-28 17:58
 */
@Service
public class LockService {
  	@Lock(key = "#user.id", keepMills = 10, maxSleepMills = 15)
    public String lock(User user) {
        System.out.println("持锁");
        return "";
    }
}

redisson分布式锁应用

分布式架构一定会用到分布式锁。目前公司使用的基于redis的redisson分布式锁。

应用场景

1.订单修改操作,首先要获取该订单的分布式锁,能取到才能去操作。lockey可以是订单的主键id。

2.库存操作,也要按照客户+仓库+sku维护锁定该库存,进行操作。

代码:

Redisson管理类

public class RedissonManager {
    private static RedissonClient redisson;
    static {
        Config config = new Config();
        config.useSentinelServers()
                .addSentinelAddress("redis://127.0.0.1:26379","redis://127.0.0.1:7301", "redis://127.0.0.1:7302")
                .setMasterName("mymaster")
                .setReadMode(ReadMode.SLAVE)
                .setTimeout(10000).setDatabase(0).setPassword("123***");
        redisson = Redisson.create(config);
    }
 
    /**
     * 获取Redisson的实例对象
     * @return
     */
    public static RedissonClient getRedisson(){ return redisson;}
}

分布式锁

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
public class DistributedLock {
    private static RedissonClient redissonClient = RedissonManager.getRedisson();
    public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
    }
    public static void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }
}

测试类

public class RedissonTest {
    public static void main(String[] args) throws Exception{
        Thread.sleep(2000L);
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    //tryLock,第三个参数是等待时间,5秒内获取不到锁,则直接返回。 第四个参数 30是30秒后强制释放
                    boolean hasLock = DistributedLock.tryLock("lockKey", TimeUnit.SECONDS,5,30);
                    //获得分布式锁
                    if(hasLock){
                        System.out.println("idea1: " + Thread.currentThread().getName() + "获得了锁");
                       
                    /**
                         * 由于在DistributedLock.tryLock设置的等待时间是5s,
                         * 所以这里如果休眠的小于5秒,这第二个线程能获取到锁,
                         *  如果设置的大于5秒,则剩下的线程都不能获取锁。可以分别试试2s,和8s的情况
                         */
                        Thread.sleep(10000L);
                        DistributedLock.unlock("lockKey");
                    } else {
                        System.out.println("idea1: " + Thread.currentThread().getName() + "无法获取锁");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } 
            }) .start();
        }
    }
}

我们再打开一个idea,可以把代码复制一份。同事启动两个RedissonTest ,模拟了并发操作。

测试结果:

idea2: Thread-1获得了锁
idea2: Thread-0无法获取锁
idea2: Thread-2无法获取锁
 
 
idea1: Thread-2无法获取锁
idea1: Thread-0无法获取锁
idea1: Thread-1无法获取锁

从测试结果发现,最后是只能有一个idea的一个线程能获取到锁。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java两个integer数据判断相等用==还是equals

    java两个integer数据判断相等用==还是equals

    本文主要介绍了java两个integer数据判断相等用==还是equals,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • SpringBoot使用自动配置xxxAutoConfiguration

    SpringBoot使用自动配置xxxAutoConfiguration

    这篇文章介绍了SpringBoot自动配置xxxAutoConfiguration的使用方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • Window中安装构建神器Jenkins详解

    Window中安装构建神器Jenkins详解

    Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建、测试和部署软件。支持各种运行方式,可通过系统包、Docker 或者通过一个独立的 Java 程序。是解放人工集成部署的自动化构建神器
    2021-07-07
  • java将文件转成流文件返回给前端详细代码实例

    java将文件转成流文件返回给前端详细代码实例

    Java编程语言提供了强大的文件处理和压缩能力,下面这篇文章主要给大家介绍了关于java将文件转成流文件返回给前端的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-07-07
  • java编程常用技术(推荐)

    java编程常用技术(推荐)

    下面小编就为大家带来一篇java编程常用技术(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • 详解JAVA后端实现统一扫码支付:微信篇

    详解JAVA后端实现统一扫码支付:微信篇

    本篇文章主要介绍了详解JAVA后端实现统一扫码支付:微信篇,这里整理了详细的代码,有需要的小伙伴可以参考下。
    2017-01-01
  • 一文搞懂Spring AOP的五大通知类型

    一文搞懂Spring AOP的五大通知类型

    本文将详细为大家介绍Spring AOP的五种通知类型(前置通知、后置通知、返回通知、异常通知、环绕通知),感兴趣的朋友可以了解一下
    2022-06-06
  • Java字符串拼接的优雅方式实例详解

    Java字符串拼接的优雅方式实例详解

    字符串拼接一般使用“+”,但是“+”不能满足大批量数据的处理,下面这篇文章主要给大家介绍了关于Java字符串拼接的几种优雅方式,需要的朋友可以参考下
    2021-07-07
  • Java多线程与优先级详细解读

    Java多线程与优先级详细解读

    这篇文章主要给大家介绍了关于Java中方法使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08
  • java编程实现两个大数相加代码示例

    java编程实现两个大数相加代码示例

    这篇文章主要介绍了java编程实现两个大数相加代码示例,具有一定参考价值,需要的朋友可以了解下。
    2017-12-12

最新评论