前言
通过使用redisson的 tryLock方法实现分布式锁。
导入依赖
<!-- redis(Spring Data Redis 的启动器) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X 集成 redis 所需 common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<!-- redisson依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.7.5</version>
</dependency>
redisson配置文件
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
/**
* Redisson的配置类
*
* @author xuyaxu
* @date 2022/10/7 14:18
*/
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password}")
private String password;
/**
* RedissonClient,单机模式
*
* @return
* @throws IOException
*/
@Bean(destroyMethod = "shutdown")
public RedissonClient redisson() throws IOException {
Config config = new Config();
config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
// 没有密码时使用下面代码,有密码使用上面代码
//config.useSingleServer().setAddress("redis://" + host + ":" + port);
return Redisson.create(config);
}
}
controller层代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 使用Redisson实现Redis分布式锁功能
*
* @author xuyaxu
* @date 2022/10/7 14:13
*/
@RestController
public class RedissonController {
@Autowired
SkillService skillService;
@RequestMapping("/testSkillService")
public void TestSkillService() {
// 使用 jmeter 模拟高并发50接口调用场景
skillService.seckill();
}
}
核心业务代码
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* 使用 redisson 获取锁、加锁和释放锁核心代码
*
* @author xuyaxu
* @date 2022/10/7 14:28
*/
@Slf4j
@Service
public class SkillService {
@Resource
RedissonClient redissonClient;
private final static String LOCK_KEY = "RESOURCE_KEY";
int n = 500;
public void seckill() {
boolean flag = false;
//定义锁
RLock lock = redissonClient.getLock(LOCK_KEY);
//lock.lock();
try {
//尝试加锁,最大等待时间3秒,上锁100秒自动解锁,时间单位为秒(参数:获得锁的最大时间;锁的存在时间;此处时间单位)
if (lock.tryLock(3, 100, TimeUnit.SECONDS)) {
log.info("线程:" + Thread.currentThread().getName() + "获得了锁");
log.info("剩余数量:{}", --n);
flag = true;
}
} catch (Exception e) {
log.error("程序执行异常:{}", e);
} finally {
// 做释放锁操作的时候判断一下是否已成功加锁
if (flag) {
log.info("线程:" + Thread.currentThread().getName() + "准备释放锁");
//释放锁
lock.unlock();
}
}
}
}
api使用说明
getLock方法
使用示例:
源码:
模拟并发
可以使用 jmeter 进行接口调用,模拟高并发的场景。
报错记录
isLocked方法的使用
报错日志:attempt to unlock lock, not locked by current thread by node id redisson.(尝试解锁锁定,当前线程未通过节点id redison锁定)
存在场景:
- 当业务处理时间超过当前线程锁的存活期时,再去释放锁会报错。
- 并发情况下,其他线程没有获得锁而走了finally块释放锁时,释放的不是自己的锁会报错。
- 并发情况下,锁被别的线程占用了,但是其他线程直接lock方法进行加锁会报错。
解决方案:使用redisson的 isLocked方法,判断声明的锁是否被锁定了,是锁定状态则再释放锁。
代码示例:
api源码:
包路径:org.redisson.api
isHeldByCurrentThread方法的使用
代码示例:
api源码:
参考文档:https://blog.csdn.net/weixin_39709686/article/details/126747127