reentrantLock的使用
reentrantLock是和锁有关,那么必然就涉及到并发操作,所以我们先构建一个项目:
依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
准备工作
往redis里面放一个值,num,值设置成100
相关代码
package com.jiubodou.bingfa.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassName MyController
* @Description
* @Author huyingliang
* @Date 2023/5/23 15:32
*/
@RestController
public class MyController {
public static ReentrantLock reentrantLock = new ReentrantLock();
@GetMapping("/")
public String sayHello() {
return "Hello,World!";
}
@Autowired
private StringRedisTemplate stringRedisTemplate;
@GetMapping("/redis")
public String redis() {
stringRedisTemplate.opsForValue().set("num", "1");
return stringRedisTemplate.opsForValue().get("num");
}
@GetMapping("/addNum")
public String addNum() {
String s = stringRedisTemplate.opsForValue().get("num");
int num = Integer.parseInt(s) + 1;
stringRedisTemplate.opsForValue().set("num", String.valueOf(num));
return stringRedisTemplate.opsForValue().get("num");
}
@GetMapping("/minusNum")
public String minusNum() {
reentrantLock.lock();
try {
String s = stringRedisTemplate.opsForValue().get("num");
int num = Integer.parseInt(s) - 1;
stringRedisTemplate.opsForValue().set("num", String.valueOf(num));
return stringRedisTemplate.opsForValue().get("num");
} finally {
reentrantLock.unlock();
}
}
@GetMapping("/addNumAuto")
public String addNumAuto() {
Long num = stringRedisTemplate.opsForValue().increment("num",1);
return String.valueOf(num);
}
@GetMapping("/minusNumAuto")
public String minusNumAuto() {
Long num = stringRedisTemplate.opsForValue().increment("num", -1);
return String.valueOf(num);
}
}
我们看第一个接口 addNum
@GetMapping("/addNum")
public String addNum() {
String s = stringRedisTemplate.opsForValue().get("num");
int num = Integer.parseInt(s) + 1;
stringRedisTemplate.opsForValue().set("num", String.valueOf(num));
return stringRedisTemplate.opsForValue().get("num");
}
很简单,就是将redis里面的值拿出来以后,加一再放回去,但是如果是并发场景下呢?
我们这里使用 ApiFox来测试一下
8个现成循环5次,也就是说,最终redis里面值应该是 100+40=140,但是结果是130
同样的,如果不加锁的情况下去执行 minusNum,
@GetMapping("/minusNum")
public String minusNum() {
String s = stringRedisTemplate.opsForValue().get("num");
int num = Integer.parseInt(s) - 1;
stringRedisTemplate.opsForValue().set("num", String.valueOf(num));
return stringRedisTemplate.opsForValue().get("num");
}
执行结果也肯定不会是 130-40=90。那么解决方案其中一个就是加锁解决这个问题
@GetMapping("/minusNum")
public String minusNum() {
reentrantLock.lock();
try {
String s = stringRedisTemplate.opsForValue().get("num");
int num = Integer.parseInt(s) - 1;
stringRedisTemplate.opsForValue().set("num", String.valueOf(num));
return stringRedisTemplate.opsForValue().get("num");
} finally {
reentrantLock.unlock();
}
}
那么有没有其他方法来解决这个并发问题呢,其实是有的,我们使用redis,其中有一个操作就是 原子性的增加/减
@GetMapping("/addNumAuto")
public String addNumAuto() {
Long num = stringRedisTemplate.opsForValue().increment("num",1);
return String.valueOf(num);
}
@GetMapping("/minusNumAuto")
public String minusNumAuto() {
Long num = stringRedisTemplate.opsForValue().increment("num", -1);
return String.valueOf(num);
}
这样也可以保证并发情况下的数据一致性