一、引入依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.5.0</version>
</dependency>
二、工具类
package com.hl.redisdemo.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.Collections;
public class RedissonLockUtil {
private static final Jedis jedis = new Jedis("localhost", 6379);
/**
* 可重入锁加锁
* @param lockKey 锁的key
* @param requestId 请求标识
* @param expireTime 超时时间
* @return 是否成功获取锁
*/
public static boolean lock(String lockKey, String requestId, int expireTime) {
SetParams params = new SetParams();
params.nx().px(expireTime); // 设置nx和px参数,保证只在不存在该key时设置值,并设置过期时间
String result = jedis.set(lockKey, requestId, params);
return "OK".equalsIgnoreCase(result);
}
/**
* 可重入锁解锁
* @param lockKey 锁的key
* @param requestId 请求标识
* @return 是否成功释放锁
*/
public static boolean unlock(String lockKey, String requestId) {
String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(luaScript, Collections.singletonList(lockKey), Collections.singletonList(requestId));
Long RELEASE_SUCCESS = 1L;
return RELEASE_SUCCESS.equals(result);
}
/**
* 获取可重入锁
* @param lockKey 锁的key
* @param requestId 请求标识
* @param expireTime 超时时间
* @return 是否成功获取锁
*/
public static boolean tryLock(String lockKey, String requestId, int expireTime) {
SetParams params = new SetParams();
params.nx().px(expireTime); // 设置nx和px参数,保证只在不存在该key时设置值,并设置过期时间
String result = jedis.set(lockKey, requestId, params);
return "OK".equalsIgnoreCase(result);
}
}
三、测试代码
package com.hl.redisdemo.controller;
import com.hl.redisdemo.util.RedissonLockUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
/**
* @author hulei
* @date 2024/2/6 17:23
*/
@RestController
public class RedisDemoController {
@RequestMapping("/redisTest")
public String redisTest() throws InterruptedException {
String str;
String lockKey = "myLock";
String requestId = UUID.randomUUID().toString(); // 生成唯一请求标识
// 集群场景下,锁超时时间应该保证大于多数节点获取锁的时间
//当发生在多数节点获取锁的时间大于锁超时时间时,获取到的锁在各节点早已超时失效,锁失效
int expireTime = 5000; // 锁的过期时间为5秒,注意,
// 尝试获取锁
if (RedissonLockUtil.tryLock(lockKey, requestId, expireTime)) {
System.out.println("获取到锁");
// 执行业务逻辑
Thread.sleep(1000);
// 释放锁
if (RedissonLockUtil.unlock(lockKey, requestId)) {
System.out.println("释放锁成功");
str = "释放锁成功";
} else {
System.out.println("释放锁失败");
str = "释放锁失败";
}
} else {
System.out.println("获取锁失败");
str = "获取锁失败";
}
return str;
}
}
测试结果分析:
1.正常测试结果

正常匀速点击请求,每次等之前的请求完成后释放锁,后面的请求则能够正常获取锁
2.异常测试结果 :


如果点击速度过快,之前的请求业务逻辑没有执行完,锁还未释放,后面的请求获取锁时,则提示锁获取失败,这样就保证了业务执行的顺序性,正确性。使用场景 如防止订单超卖,库存扣减等


















