一、引入依赖
<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.JedisPool; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.params.SetParams; import java.util.Collections; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class RedissonLockUtil { private static final JedisPool jedisPool = new JedisPool(new JedisPoolConfig(), "localhost", 6379); private static final Lock lock = new ReentrantLock(); public static boolean lock(String lockKey, String requestId, int expireTime) { try (Jedis jedis = jedisPool.getResource()) { lock.lock(); try { return tryLock(jedis, lockKey, requestId, expireTime); } finally { lock.unlock(); } } catch (Exception e) { return false; } } public static boolean unlock(String lockKey, String requestId) { try (Jedis jedis = jedisPool.getResource()) { lock.lock(); try { return releaseLock(jedis, lockKey, requestId); } finally { lock.unlock(); } } catch (Exception e) { return false; } } private static boolean tryLock(Jedis jedis, String lockKey, String requestId, int expireTime) { SetParams params = new SetParams(); params.nx().px(expireTime); String result = jedis.set(lockKey, requestId, params); return "OK".equalsIgnoreCase(result); } private static boolean releaseLock(Jedis jedis, 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); } }
三、测试代码
package com.hl.redisdemo.controller; import com.hl.redisdemo.util.RedissonLockUtil; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * @author hulei * @date 2024/2/6 17:23 */ @RestController public class RedisDemoController { @GetMapping("/redisTest") public String redisTest() throws InterruptedException { String str; String lockKey = "myLock"; // 生成唯一请求标识 String requestId = UUID.randomUUID().toString(); // 集群场景下,锁超时时间应该保证大于多数节点获取锁的时间 //当发生在多数节点获取锁的时间大于锁超时时间时,获取到的锁在各节点早已超时失效,锁失效 int expireTime = 5000; // 锁的过期时间为5秒,注意, // 尝试获取锁 if (RedissonLockUtil.lock(lockKey, requestId, expireTime)) { System.out.println("获取到锁"); // 执行业务逻辑 Thread.sleep(2000); // 释放锁 if (RedissonLockUtil.unlock(lockKey, requestId)) { System.out.println("释放锁成功"); str = "释放锁成功"; } else { System.out.println("释放锁失败"); str = "释放锁失败"; } } else { System.out.println("获取锁失败"); str = "获取锁失败"; } return str; } }
测试结果分析:
1.正常测试结果
正常匀速点击请求,每次等之前的请求完成后释放锁,后面的请求则能够正常获取锁
2.异常测试结果 :
如果点击速度过快,之前的请求业务逻辑没有执行完,锁还未释放,后面的请求获取锁时,则提示锁获取失败,这样就保证了业务执行的顺序性,正确性。使用场景 如防止订单超卖,库存扣减场景等,以上工具类使用了lua脚本方式提供了可重入锁,在提高性能的同时,更全面的保证了数据的原子性。