百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

Redisson的正确使用以及封装分布式锁

ztj100 2024-11-04 15:16 14 浏览 0 评论

设置过期时间方式

//注入RedissonClient对象    
@Resource
    private RedissonClient redissonClient;

   public void testLock5() {
        RLock lock = redissonClient.getLock("redis:qsl:test");
        try {
            boolean triedLock = lock.tryLock(1, 3, TimeUnit.SECONDS);
            if (triedLock) {
                System.out.println(String.format("线程 %s 获取到锁, 执行业务中...", Thread.currentThread().getName()));
                TimeUnit.SECONDS.sleep(5);
                System.out.println(String.format("线程 %s 业务执行完成", Thread.currentThread().getName()));
            } else {
                System.out.println("thread-id=" + Thread.currentThread().getId() + " 锁获取失败"
                );
                testLock5();
            }
        } catch (Exception e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        } finally {
            //释放锁
            if (lock.isHeldByCurrentThread()) {
                System.out.println("thread-id=" + Thread.currentThread().getId()
                        + "开始释放锁");
                lock.unlock();
                System.out.println("thread-id=" + Thread.currentThread().getId()
                        + "锁释放成功");
            }
        }
    }


    public void multiThreadLock5() throws InterruptedException {
        for (int i = 0; i < 2; i++) {
            new Thread(this::testLock5).start();
        }
        TimeUnit.SECONDS.sleep(10);
        System.out.println("main 退出");
    }

执行结果

线程 Thread-3 获取到锁, 执行业务中...
thread-id=51 锁获取失败
thread-id=51 锁获取失败
thread-id=51 锁获取失败
线程 Thread-4 获取到锁, 执行业务中...
线程 Thread-3 业务执行完成
线程 Thread-4 业务执行完成
main 退出

线程Thread-3首先获得了锁,thread-id=51后进来,取不到锁只能重试,因为代码需要执行5秒,所以到第四秒的时候,线程 Thread-3释放了锁,所以线程 Thread-4 获取到锁, 执行自己的业务,

接着5秒过后线程 Thread-3执行完成,但是他的锁已经在第三秒后被释放了,所以不用执行lock.unlock();到第8秒结束后,线程 Thread-4 业务执行完成,同样的它在第6秒结束后被释放了,所以不用执行lock.unlock();

不设置过期时间的方式

    public void multiThreadLock6() throws InterruptedException {
        for (int i = 0; i < 2; i++) {
            new Thread(this::testLock6).start();
        }
        TimeUnit.SECONDS.sleep(10);
        System.out.println("main 退出");
    }

    public void testLock6() {
        RLock lock = redissonClient.getLock("redis:qsl:test");
        try {
            boolean triedLock = lock.tryLock(1, TimeUnit.SECONDS);
            if (triedLock) {
                System.out.println(String.format("线程 %s 获取到锁, 执行业务中...", Thread.currentThread().getName()));
                try {
                    System.out.println("业务处理中...");
                } catch (Exception e) {
                    System.out.println("业务处理异常:" + e.getMessage());
                } finally {
                    lock.unlock();
                }
            } else {
                System.out.println("thread-id=" + Thread.currentThread().getId() + " 锁获取失败"
                );
            }
        } catch (Exception e) {
            System.out.println("获职锁或释放锁异常:"+e.getMessage());
        }

    }

执行结果

线程 Thread-3 获取到锁, 执行业务中...
业务处理中...
线程 Thread-4 获取到锁, 执行业务中...
业务处理中...
main 退出

可以发现Thread-3先获得了锁,在业务执行完成后,释放了锁,Thread-4才能获得锁,依次执行代码。

缺陷:这种方式没有设置锁的过期时间,所以在服务器宕机且没有释放锁的的情况下会出现死锁

我个人其实是更加推荐第一种方式,我们尽量把锁的粒度放到最小,这样出现代码执行时间超过锁超时时间的情况会比较少

自定义封装redisson

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
<!--    <parent>-->
<!--        <groupId>com.cloud.based</groupId>-->
<!--        <artifactId>cloud-based-core</artifactId>-->
<!--        <version>1.0-SNAPSHOT</version>-->
<!--    </parent>-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <artifactId>redis-distributed-lock-core</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.projectlombok</groupId>-->
<!--            <artifactId>lombok</artifactId>-->
<!--            <version>1.16.20</version>-->
<!--            <scope>provided</scope>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.5.4</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>27.0.1-jre</version>
        </dependency>
    </dependencies>
</project>

定义RedissonProperties


/**
 * @Description: 读取redis配置信息,封装到当前实体中
 *
 * @author xub
 * @date 2019/6/19 下午9:35
 */
@PropertySource({"classpath:redisson.properties"})
@ConfigurationProperties(prefix = "redisson.lock.server")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RedissonProperties {

    /**
     * redis主机地址,ip:port,有多个用半角逗号分隔
     */
    private String address;

    /**
     * 连接类型,支持standalone-单机节点,sentinel-哨兵,cluster-集群,masterslave-主从
     */
    private String type;

    /**
     * redis 连接密码
     */
    private String password;

    /**
     * 选取那个数据库
     */
    private int database;

    public RedissonProperties setPassword(String password) {
        this.password = password;
        return this;
    }

    public RedissonProperties setDatabase(int database) {
        this.database = database;
        return this;
    }

}

定义GlobalConstant全局常量枚举

package com.cloud.based.redisson.constant;


/**
 * @author xub
 * @Description: 全局常量枚举 用来拼接完整的URL
 * @date 2019/6/19 下午9:09
 */
public enum GlobalConstant {

    REDIS_CONNECTION_PREFIX("redis://", "Redis地址配置前缀");

    private final String constant_value;
    private final String constant_desc;

    GlobalConstant(String constant_value, String constant_desc) {
        this.constant_value = constant_value;
        this.constant_desc = constant_desc;
    }

    public String getConstant_value() {
        return constant_value;
    }

    public String getConstant_desc() {
        return constant_desc;
    }
}

定义RedisConnectionTypeRedis连接方式

package com.cloud.based.redisson.constant;

/**
 * @desc Redis连接方式
 *          包含:standalone-单节点部署方式
 *              sentinel-哨兵部署方式
 *              cluster-集群方式
 *              masterslave-主从部署方式
 *
 * @author xub
 * @date 2019/6/20 下午4:21
 */
public enum RedisConnectionType {

    STANDALONE("standalone", "单节点部署方式"),
    SENTINEL("sentinel", "哨兵部署方式"),
    CLUSTER("cluster", "集群方式"),
    MASTERSLAVE("masterslave", "主从部署方式");

    private final String connection_type;
    private final String connection_desc;

    private RedisConnectionType(String connection_type, String connection_desc) {
        this.connection_type = connection_type;
        this.connection_desc = connection_desc;
    }

    public String getConnection_type() {
        return connection_type;
    }

    public String getConnection_desc() {
        return connection_desc;
    }
}

定义RedissonManager管理

package com.cloud.based.redisson;


import com.cloud.based.redisson.constant.RedisConnectionType;
import com.cloud.based.redisson.properties.RedissonProperties;
import com.cloud.based.redisson.strategy.RedissonConfigService;
import com.cloud.based.redisson.strategy.impl.ClusterConfigImpl;
import com.cloud.based.redisson.strategy.impl.MasterslaveConfigImpl;
import com.cloud.based.redisson.strategy.impl.SentineConfigImpl;
import com.cloud.based.redisson.strategy.impl.StandaloneConfigImpl;
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.config.Config;


/**
 * @Description: Redisson核心配置,用于提供初始化的redisson实例
 *
 * @author xub
 * @date 2019/6/19 下午10:16
 */
@Slf4j
public class RedissonManager {


    private Config config = new Config();

    private Redisson redisson = null;

    public RedissonManager() {
    }

    public RedissonManager(RedissonProperties redissonProperties) {
        try {
            //通过不同部署方式获得不同cofig实体
            config = RedissonConfigFactory.getInstance().createConfig(redissonProperties);
            redisson = (Redisson) Redisson.create(config);
        } catch (Exception e) {
            log.error("Redisson init error", e);
            throw new IllegalArgumentException("please input correct configurations," +
                    "connectionType must in standalone/sentinel/cluster/masterslave");
        }
    }

    public Redisson getRedisson() {
        return redisson;
    }

    /**
     * Redisson连接方式配置工厂
     * 双重检查锁
     */
    static class RedissonConfigFactory {

        private RedissonConfigFactory() {
        }

        private static volatile RedissonConfigFactory factory = null;

        public static RedissonConfigFactory getInstance() {
            if (factory == null) {
                synchronized (Object.class) {
                    if (factory == null) {
                        factory = new RedissonConfigFactory();
                    }
                }
            }
            return factory;
        }


        /**
         * 根据连接类型获取对应连接方式的配置,基于策略模式
         *
         * @param redissonProperties redis连接信息
         * @return Config
         */
        Config createConfig(RedissonProperties redissonProperties) {
            Preconditions.checkNotNull(redissonProperties);
            Preconditions.checkNotNull(redissonProperties.getAddress(), "redisson.lock.server.address cannot be NULL!");
            Preconditions.checkNotNull(redissonProperties.getType(), "redisson.lock.server.password cannot be NULL");
            Preconditions.checkNotNull(redissonProperties.getDatabase(), "redisson.lock.server.database cannot be NULL");
            String connectionType = redissonProperties.getType();
            //声明配置上下文
            RedissonConfigService redissonConfigService = null;
            if (connectionType.equals(RedisConnectionType.STANDALONE.getConnection_type())) {
                redissonConfigService = new StandaloneConfigImpl();
            } else if (connectionType.equals(RedisConnectionType.SENTINEL.getConnection_type())) {
                redissonConfigService = new SentineConfigImpl();
            } else if (connectionType.equals(RedisConnectionType.CLUSTER.getConnection_type())) {
                redissonConfigService = new ClusterConfigImpl();
            } else if (connectionType.equals(RedisConnectionType.MASTERSLAVE.getConnection_type())) {
                redissonConfigService = new MasterslaveConfigImpl();
            } else {
                throw new IllegalArgumentException("创建Redisson连接Config失败!当前连接方式:" + connectionType);
            }
            return redissonConfigService.createRedissonConfig(redissonProperties);
        }
    }

}


定义RedissonConfigService Redisson配置构建接口

package com.cloud.based.redisson.strategy;


import com.cloud.based.redisson.properties.RedissonProperties;
import org.redisson.config.Config;

/**
 * @Description: Redisson配置构建接口
 *
 * @author xub
 * @date 2019/6/20 下午3:35
 */
public interface RedissonConfigService {

    /**
     * 根据不同的Redis配置策略创建对应的Config
     * @param redissonProperties
     * @return Config
     */
    Config createRedissonConfig(RedissonProperties redissonProperties);
}

定义ClusterConfigImpl cluster连接方式

package com.cloud.based.redisson.strategy.impl;



import com.cloud.based.redisson.constant.GlobalConstant;
import com.cloud.based.redisson.properties.RedissonProperties;
import com.cloud.based.redisson.strategy.RedissonConfigService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.config.Config;

/**
 * @Description: 集群方式Redisson部署
 *      地址格式:
 *          cluster方式至少6个节点(3主3从,3主做sharding,3从用来保证主宕机后可以高可用)
 *          格式为: 127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384
 *
 * @author xub
 * @date 2019/6/19 下午4:24
 */
@Slf4j
public class ClusterConfigImpl implements RedissonConfigService {

    @Override
    public Config createRedissonConfig(RedissonProperties redissonProperties) {
        Config config = new Config();
        try {
            String address = redissonProperties.getAddress();
            String password = redissonProperties.getPassword();
            String[] addrTokens = address.split(",");
            //设置cluster节点的服务IP和端口
            for (int i = 0; i < addrTokens.length; i++) {
                config.useClusterServers()
                        .addNodeAddress(GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + addrTokens[i]);
                if (StringUtil.isNotBlank(password)) {
                    config.useClusterServers().setPassword(password);
                }
            }
            log.info("初始化[集群部署]方式Config,redisAddress:" + address);
        } catch (Exception e) {
            log.error("集群部署 Redisson init error", e);
            e.printStackTrace();
        }
        return config;
    }
}

定义MasterslaveConfigImpl 主从部署

package com.cloud.based.redisson.strategy.impl;



import com.cloud.based.redisson.constant.GlobalConstant;
import com.cloud.based.redisson.properties.RedissonProperties;
import com.cloud.based.redisson.strategy.RedissonConfigService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.config.Config;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description:  主从部署Redisson配置
 *       连接方式:  主节点,子节点,子节点
 *         格式为:  127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381
 * @author xub
 * @date 2019/6/19 下午9:21
 */

@Slf4j
public class MasterslaveConfigImpl implements RedissonConfigService {

    @Override
    public Config createRedissonConfig(RedissonProperties redissonProperties) {
        Config config = new Config();
        try {
            String address = redissonProperties.getAddress();
            String password = redissonProperties.getPassword();
            int database = redissonProperties.getDatabase();
            String[] addrTokens = address.split(",");
            String masterNodeAddr = addrTokens[0];
            //设置主节点ip
            config.useMasterSlaveServers().setMasterAddress(masterNodeAddr);
            if (StringUtil.isNotBlank(password)) {
                config.useMasterSlaveServers().setPassword(password);
            }
            config.useMasterSlaveServers().setDatabase(database);
            //设置从节点,移除第一个节点,默认第一个为主节点
            List<String> slaveList = new ArrayList<>();
            for (String addrToken : addrTokens) {
                slaveList.add(GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + addrToken);
            }
            slaveList.remove(0);

            config.useMasterSlaveServers().addSlaveAddress((String[]) slaveList.toArray());
            log.info("初始化[主从部署]方式Config,redisAddress:" + address);
        } catch (Exception e) {
            log.error("主从部署 Redisson init error", e);
            e.printStackTrace();
        }
        return config;
    }

}

定义SentineConfigImpl 哨兵集群

package com.cloud.based.redisson.strategy.impl;



import com.cloud.based.redisson.constant.GlobalConstant;
import com.cloud.based.redisson.properties.RedissonProperties;
import com.cloud.based.redisson.strategy.RedissonConfigService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.config.Config;


/**
 * @Description: 哨兵集群部署Redis连接配置
 *
 * @author xub
 * @date 2019/6/19 下午9:17
 */
@Slf4j
public class SentineConfigImpl implements RedissonConfigService {



    @Override
    public Config createRedissonConfig(RedissonProperties redissonProperties) {
        Config config = new Config();
        try {
            String address = redissonProperties.getAddress();
            String password = redissonProperties.getPassword();
            int database = redissonProperties.getDatabase();
            String[] addrTokens = address.split(",");
            String sentinelAliasName = addrTokens[0];
            //设置redis配置文件sentinel.conf配置的sentinel别名
            config.useSentinelServers().setMasterName(sentinelAliasName);
            config.useSentinelServers().setDatabase(database);
            if (StringUtil.isNotBlank(password)) {
                config.useSentinelServers().setPassword(password);
            }
            //设置sentinel节点的服务IP和端口
            for (int i = 1; i < addrTokens.length; i++) {
                config.useSentinelServers().addSentinelAddress(GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + addrTokens[i]);
            }
            log.info("初始化[哨兵部署]方式Config,redisAddress:" + address);
        } catch (Exception e) {
            log.error("哨兵部署 Redisson init error", e);

        }
        return config;
    }
}

定义StandaloneConfigImpl 单机部署

package com.cloud.based.redisson.strategy.impl;



import com.cloud.based.redisson.constant.GlobalConstant;
import com.cloud.based.redisson.properties.RedissonProperties;
import com.cloud.based.redisson.strategy.RedissonConfigService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.config.Config;

/**
 * @Description: 单机部署Redisson配置
 *
 * @author xub
 * @date 2019/6/19 下午10:04
 */
@Slf4j
public class StandaloneConfigImpl implements RedissonConfigService {

    @Override
    public Config createRedissonConfig(RedissonProperties redissonProperties) {
        Config config = new Config();
        try {
            String address = redissonProperties.getAddress();
            String password = redissonProperties.getPassword();
            int database = redissonProperties.getDatabase();
            String redisAddr = GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + address;
            config.useSingleServer().setAddress(redisAddr);
            config.useSingleServer().setDatabase(database);
            //密码可以为空
            if (StringUtil.isNotBlank(password)) {
                config.useSingleServer().setPassword(password);
            }
            log.info("初始化[单机部署]方式Config,redisAddress:" + address);
        } catch (Exception e) {
            log.error("单机部署 Redisson init error", e);
        }
        return config;
    }
}

定义RedissonAutoConfiguration

package com.cloud.based.redisson.config;


import com.cloud.based.redisson.RedissonLock;
import com.cloud.based.redisson.RedissonManager;
import com.cloud.based.redisson.properties.RedissonProperties;
import org.redisson.Redisson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

/**
 * @Description: Redisson自动化配置
 *
 * @author xub
 * @date 2019/6/19 下午11:55
 */
@Configuration
@ConditionalOnClass(Redisson.class)
@EnableConfigurationProperties(RedissonProperties.class)
//@ComponentScan("com.jincou.redisson.annotation")
public class RedissonAutoConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedissonAutoConfiguration.class);

    @Bean
    @ConditionalOnMissingBean
    @Order(value = 2)
    public RedissonLock redissonLock(RedissonManager redissonManager) {
        RedissonLock redissonLock = new RedissonLock(redissonManager);
        LOGGER.info("[RedissonLock]组装完毕");
        return redissonLock;
    }

    @Bean
    @ConditionalOnMissingBean
    @Order(value = 1)
    public RedissonManager redissonManager(RedissonProperties redissonProperties) {
        RedissonManager redissonManager =
                new RedissonManager(redissonProperties);
        LOGGER.info("[RedissonManager]组装完毕,当前连接方式:" + redissonProperties.getType() +
            ",连接地址:" + redissonProperties.getAddress());
        return redissonManager;
    }
}

定义RedissonLock api

package com.cloud.based.redisson;

import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;

import java.util.concurrent.TimeUnit;

/**
 * @Description: 针对源码Redisson进行一层封装
 *
 * @author xub
 * @date 2019/6/19 下午10:26
 */
@Slf4j
public class RedissonLock {


  private  RedissonManager redissonManager;
  private Redisson redisson;


    public RedissonLock(RedissonManager redissonManager) {
        this.redissonManager = redissonManager;
        this.redisson = redissonManager.getRedisson();
    }

    public RedissonLock() {}

    /**
     * 加锁操作 (设置锁的有效时间)
     * @param lockName 锁名称
     * @param leaseTime  锁有效时间
     */
    public void lock(String lockName, long leaseTime) {
        RLock rLock = redisson.getLock(lockName);
        rLock.lock(leaseTime,TimeUnit.SECONDS);
    }

    /**
     * 加锁操作 (锁有效时间采用默认时间30秒)
     * @param lockName 锁名称
     */
    public void lock(String lockName) {
        RLock rLock = redisson.getLock(lockName);
        rLock.lock();
    }

    /**
     * 加锁操作(tryLock锁,没有等待时间)
     * @param lockName  锁名称
     * @param leaseTime 锁有效时间
     */
    public boolean tryLock(String lockName, long leaseTime) {

        RLock rLock = redisson.getLock(lockName);
        boolean getLock = false;
        try {
            getLock = rLock.tryLock( leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("获取Redisson分布式锁[异常],lockName=" + lockName, e);
            e.printStackTrace();
            return false;
        }
        return getLock;
    }

    /**
     * 加锁操作(tryLock锁,有等待时间)
     * @param lockName   锁名称
     * @param leaseTime  锁有效时间
     * @param waitTime   等待时间
     */
    public  boolean tryLock(String lockName, long leaseTime,long waitTime) {

        RLock rLock = redisson.getLock(lockName);
        boolean getLock = false;
        try {
            getLock = rLock.tryLock( waitTime,leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("获取Redisson分布式锁[异常],lockName=" + lockName, e);
            e.printStackTrace();
            return false;
        }
        return getLock;
    }

    /**
     * 解锁
     * @param lockName  锁名称
     */
    public void unlock(String lockName) {
        redisson.getLock(lockName).unlock();
    }

    /**
     * 判断该锁是否已经被线程持有
     * @param lockName  锁名称
     */
    public boolean isLock(String lockName) {
        RLock rLock = redisson.getLock(lockName);
        return rLock.isLocked();
    }


    /**
     * 判断该线程是否持有当前锁
     * @param lockName  锁名称
     */
    public boolean isHeldByCurrentThread(String lockName) {
        RLock rLock = redisson.getLock(lockName);
        return rLock.isHeldByCurrentThread();
    }

    public RedissonManager getRedissonManager() {
        return redissonManager;
    }

    public void setRedissonManager(RedissonManager redissonManager) {
        this.redissonManager = redissonManager;
    }
}

自定义注解DistributedLock

package com.cloud.based.redisson.annotation;

import java.lang.annotation.*;

/**
 * @Description: 基于注解的分布式式锁
 *
 * @author xub
 * @date 2019/6/19 下午9:22
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DistributedLock {

    /**
     * 锁的名称
     */
    String value() default "redisson";

    /**
     * 锁的有效时间
     */
    int leaseTime() default 10;

    /**
     * 锁的等待时间
     * @return
     */
    int waitTime() default 0;
}


自定义aop DistributedLockHandler

package com.cloud.based.redisson.annotation;


import com.cloud.based.redisson.RedissonLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


/**
 * @Description: Redisson分布式锁注解解析器
 *
 * @author xub
 * @date 2019/6/20 下午9:34
 */
@Aspect
@Component
@Slf4j
public class DistributedLockHandler {

    @Autowired
    RedissonLock redissonLock;


    @Around("@annotation(distributedLock)")
    public void around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
        log.info("[开始]执行RedisLock环绕通知,获取Redis分布式锁开始");
        //获取锁名称
        String lockName = distributedLock.value();
        //获取超时时间,默认10秒
        int leaseTime = distributedLock.leaseTime();
        int waitTime = distributedLock.waitTime();
        if(0==waitTime){
            //如果没有设置等待时间,则直接加锁
            redissonLock.lock(lockName, leaseTime);
        }else {
            //加锁并设置锁的最大等待时间
            redissonLock.tryLock(lockName, leaseTime,waitTime);
        }
        try {
            log.info("获取Redis分布式锁[成功],加锁完成,开始执行业务逻辑...");
            joinPoint.proceed();
        } catch (Throwable throwable) {
            log.error("获取Redis分布式锁[异常],加锁失败", throwable);
            throwable.printStackTrace();
        } finally {
            //如果该线程还持有该锁,那么释放该锁。如果该线程不持有该锁,说明该线程的锁已到过期时间,自动释放锁
            if (redissonLock.isHeldByCurrentThread(lockName)) {
                redissonLock.unlock(lockName);
            }
        }
        log.info("释放Redis分布式锁[成功],解锁完成,结束业务逻辑...");
    }
}

在resources->META-INF新增spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.cloud.based.redisson.config.RedissonAutoConfiguration

在需要的服务yml中新增

redisson:
  lock:
    server:
      address: xxx:xxx
      type: standalone
      password: xxxxx
      database: 1

代码中直接添加注解使用

package com.cloud.based.controller;


import com.cloud.based.redisson.RedissonLock;
import com.cloud.based.redisson.annotation.DistributedLock;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author xub
 * @Description: 基于注解的方式 加锁
 * @date 2019/6/19 下午11:01
 */
@RestController
@Slf4j
@Api(description = "redisson注解管理", tags = "AnnotatinLockController")
@AllArgsConstructor
public class AnnotatinLockController {


    private  final RedissonLock redissonLock;

    /**
     * 模拟这个是商品库存
     */
    public static volatile Integer TOTAL = 10;

    @ApiOperation("注释性锁定减少库存")
    @GetMapping("annotatin-lock-decrease-stock")
    @DistributedLock(value="goods", leaseTime=5)
    public String lockDecreaseStock() throws InterruptedException {
        if (TOTAL > 0) {
            TOTAL--;
        }
        log.info("===注解模式=== 减完库存后,当前库存===" + TOTAL);
        return "=================================";
    }
}

大功告成

参考代码:

https://gitee.com/youzhibing/qsl-project/tree/master/redisson-spring-boot-demo

https://gitee.com/yangxixi_323/spring-boot-distributed-redisson?_from=gitee_search

参考博客:

https://juejin.cn/post/7394094789270372403

https://www.cnblogs.com/qdhxhz/p/11059200.html

相关推荐

从IDEA开始,迈进GO语言之门(idea got)

前言笔者在学习GO语言编程的时候,GO语言在国内还没有像JAVA/Php/Python那样普及,绕了不少的弯路,要开始入门学习一门编程语言,最好就先从选择一个好的编程语言的开发环境开始,有了这个开发环...

基于SpringBoot+MyBatis的私人影院java网上购票jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于SpringBoot...

基于springboot的个人服装管理系统java网上商城jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于springboot...

基于springboot的美食网站Java食品销售jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于springboot...

贸易管理进销存springboot云管货管账分析java jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目描述贸易管理进销存spring...

SpringBoot+VUE员工信息管理系统Java人员管理jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍SpringBoot+V...

目前见过最牛的一个SpringBoot商城项目(附源码)还有人没用过吗

帮粉丝找了一个基于SpringBoot的天猫商城项目,快速部署运行,所用技术:MySQL,Druid,Log4j2,Maven,Echarts,Bootstrap...免费给大家分享出来前台演示...

SpringBoot+Mysql实现的手机商城附带源码演示导入视频

今天为大家带来的是基于SpringBoot+JPA+Thymeleaf框架的手机商城管理系统,商城系统分为前台和后台、前台用的是Bootstrap框架后台用的是SpringBoot+JPA都是现在主...

全网首发!马士兵内部共享—1658页《Java面试突击核心讲》

又是一年一度的“金九银十”秋招大热门,为助力广大程序员朋友“面试造火箭”,小编今天给大家分享的便是这份马士兵内部的面试神技——1658页《Java面试突击核心讲》!...

SpringBoot数据库操作的应用(springboot与数据库交互)

1.JDBC+HikariDataSource...

SpringBoot 整合 Flink 实时同步 MySQL

1、需求在Flink发布SpringBoot打包的jar包能够实时同步MySQL表,做到原表进行新增、修改、删除的时候目标表都能对应同步。...

SpringBoot + Mybatis + Shiro + mysql + redis智能平台源码分享

后端技术栈基于SpringBoot+Mybatis+Shiro+mysql+redis构建的智慧云智能教育平台基于数据驱动视图的理念封装element-ui,即使没有vue的使...

Springboot+Mysql舞蹈课程在线预约系统源码附带视频运行教程

今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的Springboot+Mysql舞蹈课程在线预约系统,系统项目源代码在【猿来入此】获取!https://www.yuan...

SpringBoot+Mysql在线众筹系统源码+讲解视频+开发文档(参考论文

今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的在线众筹管理系统,主要实现了普通用户在线参与众筹基本操作流程的全部功能,系统分普通用户、超级管理员等角色,除基础脚手架外...

Docker一键部署 SpringBoot 应用的方法,贼快贼好用

这两天发现个Gradle插件,支持一键打包、推送Docker镜像。今天我们来讲讲这个插件,希望对大家有所帮助!GradleDockerPlugin简介...

取消回复欢迎 发表评论: