一、引入依赖
<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.异常测试结果 :
如果点击速度过快,之前的请求业务逻辑没有执行完,锁还未释放,后面的请求获取锁时,则提示锁获取失败,这样就保证了业务执行的顺序性,正确性。使用场景 如防止订单超卖,库存扣减等