实现方式
- redis的increment方式抢占锁成功返回1失败返回0
- setNx方式抢占锁
- redission框架的使用
pom.xml文件
<?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>
<groupId>com</groupId>
<artifactId>redissionLock</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties文件
server.port=9090
#redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=2
spring.redis.password=
spring.redis.timeout=1s
spring.redis.lettuce.pool.min-idle=1
spring.redis.lettuce.pool.max-active=500
TestController.java addLock()采用increment方式
@RestController
public class TestController {
@Resource
RedisTemplate<String, String> redisTemplate;
String redisKey = "basedata:productKey";
String number = "basedata:number";
@RequestMapping("/addProduct")
public void addProduct() throws RuntimeException {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.opsForValue().set(number, 10 + "");
}
@RequestMapping("/addLock")
public void addLock() throws RuntimeException {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
try {
long num = redisTemplate.opsForValue().increment(redisKey, 1);
redisTemplate.expire(redisKey, 2, TimeUnit.SECONDS);//防止死锁
if (num == 1) {
int stack = Integer.parseInt(redisTemplate.opsForValue().get(number));
if (stack > 1) {
redisTemplate.opsForValue().set(number, (stack - 1) + "");
System.out.println("成功" + (stack - 1));
} else {
System.out.println("失败");
}
}
} finally {
redisTemplate.opsForValue().increment(redisKey, -1);
}
}
}
注:redisTemplate.expire(redisKey, 2, TimeUnit.SECONDS);//防止死锁其实还是会有bug;当前节点执行超时,另一个节点获取了锁,而当节点恰好执行了finall的释放锁,导致其他节点也获取了锁,数据错误,因此需要不停更新超时时间
public class ThreadWorker extends Thread {
private String redisKey;
private boolean stopflag;
RedisTemplate<String, String> redisTemplate;
public ThreadWorker(String redisKey ,RedisTemplate<String, String> redisTemplate) {
this.redisKey = redisKey;
this.redisTemplate=redisTemplate;
}
@Override
public void run() {
while(stopflag!=false) {
Boolean expire = redisTemplate.expire(redisKey, 60, TimeUnit.SECONDS);
if(!expire) {
break;
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println(e);
}
}
}
public String getRedisKey() {
return redisKey;
}
public void setRedisKey(String redisKey) {
this.redisKey = redisKey;
}
public boolean isStopflag() {
return stopflag;
}
public void setStopflag(boolean stopflag) {
this.stopflag = stopflag;
}
}
setNx方式抢占锁,会发生锁抢占失败
public void addSetNexLock1() throws RuntimeException {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
ThreadWorker worker = null;
Boolean setNX = getLock(redisTemplate);
try {
worker = new ThreadWorker(redisKey, redisTemplate);
worker.start();
if (setNX) {
int stack = Integer.parseInt(redisTemplate.opsForValue().get(number));
if (stack > 1) {
redisTemplate.opsForValue().set(number, (stack - 1) + "");
System.out.println("成功" + (stack - 1));
} else {
System.out.println("失败");
}
} else {
list.add("1");
}
} finally {
if (setNX) {
worker.setStopflag(true);
redisTemplate.delete(redisKey);
}
}
}
自旋抢锁方式
private Boolean getLock(RedisTemplate<String, String> redisTemplate) {
RedisConnectionFactory ConnectionFactory= redisTemplate.getConnectionFactory();
Boolean setNX = ConnectionFactory.getConnection().setNX(redisKey.getBytes(), "1".getBytes());
while (true) {
if (!setNX) {
setNX = setIfAbsent.getConnection().setNX(redisKey.getBytes(), "1".getBytes());
} else {
break;
}
}
return setNX;
}
Redission方式
加入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.10.6</version>
</dependency>
@RequestMapping("/addRedissionLock")
public void addRedissionLock() throws RuntimeException, InterruptedException {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
/*
* HashOperations<String, Object, Object> opsForHash =
* redisTemplate.opsForHash(); opsForHash.putIfAbsent(redisKey, hashKey, 1);
*/
// 创建锁对象,并制定锁的名称
RLock tasklock = redissonClient.getLock("taskLock");
// 获取锁,设置自动失效时间为50s
boolean isLock = tasklock.tryLock(50, TimeUnit.SECONDS);
try {
if (isLock) {
int stack = Integer.parseInt(redisTemplate.opsForValue().get(number));
if (stack > 1) {
redisTemplate.opsForValue().set(number, (stack - 1) + "");
System.out.println("成功" + (stack - 1));
} else {
System.out.println("失败");
}
}
} finally {
tasklock.unlock();
}
}
介绍一款测压工具Apache JMeter