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简介...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 从IDEA开始,迈进GO语言之门(idea got)
- 基于SpringBoot+MyBatis的私人影院java网上购票jsp源代码Mysql
- 基于springboot的个人服装管理系统java网上商城jsp源代码mysql
- 基于springboot的美食网站Java食品销售jsp源代码Mysql
- 贸易管理进销存springboot云管货管账分析java jsp源代码mysql
- SpringBoot+VUE员工信息管理系统Java人员管理jsp源代码Mysql
- 目前见过最牛的一个SpringBoot商城项目(附源码)还有人没用过吗
- SpringBoot+Mysql实现的手机商城附带源码演示导入视频
- 全网首发!马士兵内部共享—1658页《Java面试突击核心讲》
- SpringBoot数据库操作的应用(springboot与数据库交互)
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- node卸载 (33)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- exceptionininitializererror (33)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)