Redis分布式锁
前言
工作将近两年的时间了,都没想着可以写些什么东西,但是光凭记忆还是容易遗忘的,现就总结下前面阶段学习的东西供后面进行查阅 。
关于Redis目前还了解的不太多,仅针对自己当前项目中用到的Redis分布式锁的实现进行讲解,后面有机会再专门记录下Redis相关的东西。
本篇主要使用Redis的setnx + get + getset命令来实现分布式锁的,主要步骤如下:
1、入redis的依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2、实现Redis的加锁和解锁的方法,这里使用StringRedisTemplate类来实现
加锁:
- 先使用setnx(get if not exist)命令来尝试获取锁,返回1则说明加锁成功,成功将值set进去;0则说明key以存在,加锁失败不做任何操作
- 如果没有获取到锁,则看锁是否过期了,如果锁过期了,则可以让新的请求来重新获取锁
- 新的请求过来,且锁已经过期,若在get和getset之间是原子的(没有其他的线程获取该锁),那个就可以获取锁
解锁:
- 解锁就是直接将对应的key删除即可,但是前提是自己只能解自己加的锁
实现:
1 import lombok.extern.slf4j.Slf4j; 2 import org.springframework.beans.factory.annotation.Autowired; 3 import org.springframework.data.redis.core.StringRedisTemplate; 4 import org.springframework.stereotype.Component; 5 import org.springframework.util.StringUtils; 6 7 @Component 8 @Slf4j 9 public class RedisLock 10 { 11 @Autowired 12 private StringRedisTemplate redisTemplate; 13 14 /** 15 * 加锁 16 * @param key 17 * @param value 当前时间+超时时间 18 * @return 19 */ 20 public boolean lock(String key, String value) 21 { 22 //首次请求,获取锁 23 if (redisTemplate.opsForValue().setIfAbsent(key,value)) 24 { 25 return true; 26 } 27 28 //没获取到锁,判断是否锁过期了 29 String currentValue = redisTemplate.opsForValue().get(key); 30 if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) 31 { 32 //锁过期,说明可以供新的请求来重新获取锁 33 String oldValue = redisTemplate.opsForValue().getAndSet(key, value); 34 if(!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) 35 { 36 //新请求get()的值与getset()返回的值相同,说明已经重新获取到了锁 37 return true; 38 } 39 } 40 41 return false; 42 } 43 44 /** 45 * 解锁 46 * @param key 47 * @param value 48 */ 49 public void unlock(String key, String value) 50 { 51 try 52 { 53 String currentValue = redisTemplate.opsForValue().get(key); 54 if(!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) 55 { 56 //加锁和解锁是成对存在的,这里get()的值就是加锁时set()进去的值 57 redisTemplate.opsForValue().getOperations().delete(key); 58 } 59 }catch(Exception e) 60 { 61 log.error("【redis分布式锁】解锁异常,{}", e); 62 } 63 } 64 }
3、调用锁
1 import org.junit.Test; 2 import org.junit.runner.RunWith; 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.boot.test.context.SpringBootTest; 5 import org.springframework.test.context.junit4.SpringRunner; 6 7 @RunWith(SpringRunner.class) 8 @SpringBootTest 9 public class RedisLockTest 10 { 11 /** 超时时间10s. */ 12 private static final int TIMEOUT = 10 * 1000; 13 14 @Autowired 15 private RedisLock redisLock; 16 17 @Test 18 public void testRedisLock() throws Exception 19 { 20 //加锁 21 String key = "localKey"; 22 long time = System.currentTimeMillis() + TIMEOUT; 23 if(!redisLock.lock(key, String.valueOf(time))) 24 { 25 throw new Exception("哎哟喂,人也太多了,换个姿势再试试~~"); 26 } 27 28 //业务逻辑略 29 30 //解锁 31 redisLock.unlock(key, String.valueOf(time)); 32 } 33 }
浙公网安备 33010602011771号