一、使用StringRedisTemplate实现分布式锁
1. 使用docker安装redis
① 拉取镜像
docker pull redis:latest
②查看本地镜像
docker images

③运行镜像
docker run -itd --name redis-test -p 6379:6379 redis
参数说明:
- -p 6379:6379:映射容器服务的 6379 端口到宿主机的 6379 端口。外部可以直接通过宿主机ip:6379 访问到 Redis 的服务。
④安装成功
使用 docker ps 查看正在运行的容器
通过 redis-cli 连接测试使用 redis 服务
docker exec -it redis-test /bin/bash
2. 新建spring boot项目
①引入依赖
<!----redis依赖------->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
②编写配置文件
spring:
redis:
database: 5
host: 192.168.0.100
port: 6379
password:
timeout: 5000
jedis:
pool:
max-idle: 500
min-idle: 50
max-active: 1000
max-wait: 2000
③编写逻辑代码
@Service
public class RecordServiceImpl implements RecordService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void consumeAmount(Long userId, BigDecimal amount) {
String key = "lock_"+userId;
String value = UUID.randomUUID().toString();
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key, value, 10, TimeUnit.SECONDS);
while (!result) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
result = stringRedisTemplate.opsForValue().setIfAbsent(key, value, 10, TimeUnit.SECONDS);
}
try {
//...........
//逻辑代码
}finally {
if (value.equals(stringRedisTemplate.opsForValue().get(key))) {
stringRedisTemplate.delete(key);
}
}
}
}
④代码解析
- 每个线程锁唯一
String value = UUID.randomUUID().toString();
保证每个线程只释放自己的锁,避免代码执行时间大于锁有效时间,导致锁永久失效。
//释放对应线程自己的锁
if (value.equals(stringRedisTemplate.opsForValue().get(key))) {
stringRedisTemplate.delete(key);
}
- 加锁及添加过期时间用一条语句
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key, value, 10, TimeUnit.SECONDS);
保证原子性,如果在key赋值和过期时间为两条语句,在该两条语句之间出现服务器宕机,导致锁永不过期,下面逻辑永远无法执行。
循环获取锁
while (!result) { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } result = stringRedisTemplate.opsForValue().setIfAbsent(key, value, 10, TimeUnit.SECONDS); }若线程获取锁失败,则循环获取锁,保证每条线程都可以执行逻辑代码,保证一致性。循环执行时间一般为锁存活时间1/3 。
在try内编写逻辑,finally释放锁
try { //........... //逻辑代码 }finally { if (value.equals(stringRedisTemplate.opsForValue().get(key))) { stringRedisTemplate.delete(key); }避免逻辑代码内出现异常而无法释放锁。
