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 }

 

posted @ 2018-11-26 20:06  天马行空~_~  阅读(381)  评论(0)    收藏  举报