Redis分布式锁实现

本文探讨了Redis分布式锁的实现中需要注意的问题,包括锁的安全性、不失效和超时设置。同时,提出了分布式锁应满足的互斥性、防止死锁和防止误解锁的条件。文章还介绍了配置RedisLock的步骤,并通过JMeter模拟环境进行测试验证。
摘要由CSDN通过智能技术生成

分布式锁的实现

  • 需要注意的点

1、锁不安全:
任意客户端获得锁后,在释放锁之前该服务一旦崩溃宕机,锁将永远都不会释放简单来讲:其他服务都将竞争不到锁,竞争不到锁的线程在不断自旋,与此同时还会不断有新的请求接入,最终导致服务宕机,Redis宕机
2、锁不失效:
假设过期时间10S,服务A可能在10S内还没搞完业务逻辑,锁超时后服务B获得了锁,这时服务A执行完了业务逻辑,这样错误的释放了由服务B加的锁,就会导致锁失效。
3、锁超时:
太长的话 如果某个服务宕机后,其他服务需要等待很久才能获得锁 就没有必要
太短的话 可能大部分服务还没有执行完业务逻辑,锁还来不及释放就过期了,产生并发安全问题

  • 需要满足的条件

1、互斥性:在任何时候,只有一个客户端可以获得锁
2、防止死锁:即使获得锁的客户端在没有释放锁之前崩溃宕机,后续客户端继续可以竞争到锁
3、防止误解别人的锁,或者是自己还需要用的锁:释放锁的客户端一定是加锁的客户端,不能错误的释放其他客户端加的锁

开始准备工作:

添加RedisLock配置类 定义方法

@Component
public class RedisLock {

    @Autowired
    private RedisTemplate redisTemplate; //定义redis模板

    //线程安全的map
    private ConcurrentMap<String, String> keyMap = new ConcurrentHashMap<>(16);

    //守护线程map
    private ConcurrentMap<String, ContinueThread> threadMap = new ConcurrentHashMap<>(16);

    //加锁
    public String addLock(String key) {
        ValueOperations<String, Object> kv = redisTemplate.opsForValue();
        String valueSign = UUID.randomUUID().toString();
        //锁超时10s
        //10s后未释放锁,其他线程可以竞争
        while (!kv.setIfAbsent(key, valueSign, 10, TimeUnit.SECONDS)) {
        	//竞争锁失败的话,就暂时让出CPU资源 休息一下
            Thread.yield(); 
        }
        //put 竞争锁成功
        keyMap.put(key, valueSign);
        //开启守护线程
        ContinueThread continueThread = new ContinueThread(key, valueSign);
        continueThread.setDaemon(true);
        //put 守护线程成功
        threadMap.put(key, continueThread);
        continueThread.start();
        return valueSign; //返回value
    }

    //解锁
    public void unLock(String key, String value) {
        String keyLock = keyMap.get(key);
        System.out.println("开始解锁=============");
        System.out.println("解锁key:" + key);
        System.out.println("解锁keyLock:" + keyLock);
        System.out.println("解锁value:" + value);
        if (!value.equals(keyLock)) return; //这个时候出去 是因为不是我自己的锁 所以不释放 不要把别人的锁释放掉了
        redisTemplate.delete(key); //是我加的锁,可以释放
        //这个时候就可以把守护线程关掉了
        ContinueThread thread = threadMap.get(key);
        System.out.println("解锁thread:" + thread);
        //判断一下 然后关掉 不要直接关掉 严谨一点
        Optional.ofNullable(thread).ifPresent(t1 -> {
            t1.stopT();
        });
        System.out.println("结束解锁=============");
    }


    //续命 内部类 守护线程
    private class ContinueThread extends Thread {
        private boolean flag = false; //定义一个开关
        private String key;
        private String value;
        public ContinueThread(String key, String value) {
            this.key = key;
            this.value = value;
        }
        //调用这个关掉线程
        public void stopT() {
            this.flag = true;
        }
        @Override
        public void run() {
            while (!flag) {
                System.err.println("isInterrupted:" + !isInterrupted());
                try {
                    //三秒
                    Thread.sleep(3 * 1000);
                } catch (InterruptedException  i) {
                    System.err.println("这个时候在续命了.....");
                    redisTemplate.opsForValue().set(key, value, 10, TimeUnit.SECONDS);
                }
            }
        }
    }
}

一、 注入RedisLock依赖

@Autowired
private RedisLock redisLock;

二、这里自定义变量

private static final String KEY= "key";
private static final String LOCK = "lock";

三、 为了方便配置初始化redis数据

@PostConstruct
public void init() {
    redisTemplate.delete(KEY);
    redisTemplate.opsForValue().set(KEY, String.valueOf(20));
}

代码实现

public void redisLock() {
    //拿到返回的value
    String addLock = redisLock.addLock(LOCK);
    System.out.println("返回的sign:" + addLock);
    Integer number;
    //加锁
    number = Integer.parseInt(redisTemplate.opsForValue().get(KEY).toString());
    System.out.println("number:" + number);
    //小于等于0 这个时候没了
    if (number <= 0) return;
    number = new BigDecimal(redisTemplate.opsForValue().decrement(KEY).toString()).intValue();
    System.out.println("成功,剩余:" + number);
    redisLock.unLock(LOCK, addLock);
    System.out.println("去掉key:" + KEY);
    System.out.println("执行完毕");
}

使用Jemter模拟环境,最后贴出运行结果

加锁keyMap:[lock=70ff64b1-9332-49bc-a34d-d947a1d03af3]
加锁threadMap:[lock=Thread[Thread-19,5,main]]
返回的sign:70ff64b1-9332-49bc-a34d-d947a1d03af3
number:20
成功,剩余:19
开始解锁=============
解锁key:lock
解锁keyLock:70ff64b1-9332-49bc-a34d-d947a1d03af3
解锁value:70ff64b1-9332-49bc-a34d-d947a1d03af3
解锁thread:Thread[Thread-19,5,main]
结束解锁=============
去掉key:hy
执行完毕
isInterrupted:true
加锁keyMap:[lock=05289b4d-d21e-4bc7-93e3-419483fc952f]
加锁threadMap:[lock=Thread[Thread-20,5,main]]
返回的sign:05289b4d-d21e-4bc7-93e3-419483fc952f
number:19
成功,剩余:18
开始解锁=============
解锁key:lock
解锁keyLock:05289b4d-d21e-4bc7-93e3-419483fc952f
解锁value:05289b4d-d21e-4bc7-93e3-419483fc952f
解锁thread:Thread[Thread-20,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=a13724bc-9b44-4139-89a2-c9b7a202fdc5]
加锁threadMap:[lock=Thread[Thread-21,5,main]]
返回的sign:a13724bc-9b44-4139-89a2-c9b7a202fdc5
number:18
成功,剩余:17
开始解锁=============
解锁key:lock
解锁keyLock:a13724bc-9b44-4139-89a2-c9b7a202fdc5
解锁value:a13724bc-9b44-4139-89a2-c9b7a202fdc5
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
解锁thread:Thread[Thread-21,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=c5cf5452-12f0-4bd5-ada1-39fae37d208d]
加锁threadMap:[lock=Thread[Thread-22,5,main]]
返回的sign:c5cf5452-12f0-4bd5-ada1-39fae37d208d
number:17
成功,剩余:16
开始解锁=============
解锁key:lock
解锁keyLock:c5cf5452-12f0-4bd5-ada1-39fae37d208d
解锁value:c5cf5452-12f0-4bd5-ada1-39fae37d208d
解锁thread:Thread[Thread-22,5,main]
结束解锁=============
去掉key:hy
加锁keyMap:[lock=bb260223-4a29-4525-91a4-5b0a95ea1edb]
执行完毕
加锁threadMap:[lock=Thread[Thread-23,5,main]]
返回的sign:bb260223-4a29-4525-91a4-5b0a95ea1edb
number:16
成功,剩余:15
开始解锁=============
解锁key:lock
解锁keyLock:bb260223-4a29-4525-91a4-5b0a95ea1edb
解锁value:bb260223-4a29-4525-91a4-5b0a95ea1edb
解锁thread:Thread[Thread-23,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=32fdc719-0018-4b70-82c8-17c90ab241d0]
加锁threadMap:[lock=Thread[Thread-24,5,main]]
返回的sign:32fdc719-0018-4b70-82c8-17c90ab241d0
number:15
成功,剩余:14
开始解锁=============
解锁key:lock
解锁keyLock:32fdc719-0018-4b70-82c8-17c90ab241d0
解锁value:32fdc719-0018-4b70-82c8-17c90ab241d0
解锁thread:Thread[Thread-24,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=b916d222-a416-4585-8b14-1e97d7dfd235]
加锁threadMap:[lock=Thread[Thread-25,5,main]]
返回的sign:b916d222-a416-4585-8b14-1e97d7dfd235
number:14
成功,剩余:13
开始解锁=============
解锁key:lock
解锁keyLock:b916d222-a416-4585-8b14-1e97d7dfd235
解锁value:b916d222-a416-4585-8b14-1e97d7dfd235
解锁thread:Thread[Thread-25,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=a0267cee-c106-412f-8ee0-5da4f7df2370]
加锁threadMap:[lock=Thread[Thread-26,5,main]]
返回的sign:a0267cee-c106-412f-8ee0-5da4f7df2370
number:13
成功,剩余:12
开始解锁=============
解锁key:lock
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
解锁keyLock:a0267cee-c106-412f-8ee0-5da4f7df2370
解锁value:a0267cee-c106-412f-8ee0-5da4f7df2370
解锁thread:Thread[Thread-26,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=3ce70e99-d94d-41aa-a3d7-6f4469c29fc5]
加锁threadMap:[lock=Thread[Thread-27,5,main]]
返回的sign:3ce70e99-d94d-41aa-a3d7-6f4469c29fc5
number:12
成功,剩余:11
开始解锁=============
解锁key:lock
解锁keyLock:3ce70e99-d94d-41aa-a3d7-6f4469c29fc5
解锁value:3ce70e99-d94d-41aa-a3d7-6f4469c29fc5
解锁thread:Thread[Thread-27,5,main]
结束解锁=============
去掉key:hy
加锁keyMap:[lock=3bf1c5da-807e-4428-8f9f-6f7e3d9e9858]
执行完毕
加锁threadMap:[lock=Thread[Thread-28,5,main]]
返回的sign:3bf1c5da-807e-4428-8f9f-6f7e3d9e9858
number:11
成功,剩余:10
开始解锁=============
解锁key:lock
解锁keyLock:3bf1c5da-807e-4428-8f9f-6f7e3d9e9858
解锁value:3bf1c5da-807e-4428-8f9f-6f7e3d9e9858
解锁thread:Thread[Thread-28,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=5b9876a4-223c-4661-8330-7edc4afa00de]
加锁threadMap:[lock=Thread[Thread-29,5,main]]
返回的sign:5b9876a4-223c-4661-8330-7edc4afa00de
number:10
成功,剩余:9
开始解锁=============
解锁key:lock
解锁keyLock:5b9876a4-223c-4661-8330-7edc4afa00de
解锁value:5b9876a4-223c-4661-8330-7edc4afa00de
解锁thread:Thread[Thread-29,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=55dea574-9c73-4f5e-b33c-42079a219fab]
加锁threadMap:[lock=Thread[Thread-30,5,main]]
返回的sign:55dea574-9c73-4f5e-b33c-42079a219fab
number:9
成功,剩余:8
开始解锁=============
解锁key:lock
解锁keyLock:55dea574-9c73-4f5e-b33c-42079a219fab
解锁value:55dea574-9c73-4f5e-b33c-42079a219fab
加锁keyMap:[lock=8bb8cc39-e94d-49ee-9fd4-f3efd827547b]
解锁thread:Thread[Thread-30,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁threadMap:[lock=Thread[Thread-31,5,main]]
返回的sign:8bb8cc39-e94d-49ee-9fd4-f3efd827547b
number:8
成功,剩余:7
开始解锁=============
解锁key:lock
解锁keyLock:8bb8cc39-e94d-49ee-9fd4-f3efd827547b
解锁value:8bb8cc39-e94d-49ee-9fd4-f3efd827547b
解锁thread:Thread[Thread-31,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=c5d91c6a-dfd8-481c-9dfa-a9b42d3ba522]
加锁threadMap:[lock=Thread[Thread-32,5,main]]
返回的sign:c5d91c6a-dfd8-481c-9dfa-a9b42d3ba522
number:7
成功,剩余:6
开始解锁=============
解锁key:lock
解锁keyLock:c5d91c6a-dfd8-481c-9dfa-a9b42d3ba522
解锁value:c5d91c6a-dfd8-481c-9dfa-a9b42d3ba522
解锁thread:Thread[Thread-32,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=aaf0c8e1-08f8-4e1f-b59f-e3ebca1c0c78]
加锁threadMap:[lock=Thread[Thread-33,5,main]]
返回的sign:aaf0c8e1-08f8-4e1f-b59f-e3ebca1c0c78
number:6
成功,剩余:5
开始解锁=============
解锁key:lock
解锁keyLock:aaf0c8e1-08f8-4e1f-b59f-e3ebca1c0c78
解锁value:aaf0c8e1-08f8-4e1f-b59f-e3ebca1c0c78
解锁thread:Thread[Thread-33,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=9a924011-c9e7-4954-a3a6-6621937db0e8]
加锁threadMap:[lock=Thread[Thread-34,5,main]]
返回的sign:9a924011-c9e7-4954-a3a6-6621937db0e8
number:5
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
成功,剩余:4
开始解锁=============
解锁key:lock
解锁keyLock:9a924011-c9e7-4954-a3a6-6621937db0e8
解锁value:9a924011-c9e7-4954-a3a6-6621937db0e8
解锁thread:Thread[Thread-34,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=f1da0948-1c2c-45a9-b546-f4774307a1eb]
加锁threadMap:[lock=Thread[Thread-35,5,main]]
返回的sign:f1da0948-1c2c-45a9-b546-f4774307a1eb
number:4
成功,剩余:3
开始解锁=============
解锁key:lock
解锁keyLock:f1da0948-1c2c-45a9-b546-f4774307a1eb
解锁value:f1da0948-1c2c-45a9-b546-f4774307a1eb
解锁thread:Thread[Thread-35,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=9857d21c-def8-4ab8-8e31-42fd52ef2a7f]
加锁threadMap:[lock=Thread[Thread-36,5,main]]
返回的sign:9857d21c-def8-4ab8-8e31-42fd52ef2a7f
number:3
成功,剩余:2
开始解锁=============
解锁key:lock
解锁keyLock:9857d21c-def8-4ab8-8e31-42fd52ef2a7f
解锁value:9857d21c-def8-4ab8-8e31-42fd52ef2a7f
解锁thread:Thread[Thread-36,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=e323948f-85ac-45f3-a707-76754a1cd7cb]
加锁threadMap:[lock=Thread[Thread-37,5,main]]
返回的sign:e323948f-85ac-45f3-a707-76754a1cd7cb
number:2
成功,剩余:1
开始解锁=============
解锁key:lock
解锁keyLock:e323948f-85ac-45f3-a707-76754a1cd7cb
解锁value:e323948f-85ac-45f3-a707-76754a1cd7cb
解锁thread:Thread[Thread-37,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=9a0a3219-55a6-4d96-945c-510984937fbe]
加锁threadMap:[lock=Thread[Thread-38,5,main]]
返回的sign:9a0a3219-55a6-4d96-945c-510984937fbe
number:1
成功,剩余:0
开始解锁=============
解锁key:lock
解锁keyLock:9a0a3219-55a6-4d96-945c-510984937fbe
解锁value:9a0a3219-55a6-4d96-945c-510984937fbe
解锁thread:Thread[Thread-38,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=1384f3a2-8d5e-43be-968e-2fc2d25d4826]
加锁threadMap:[lock=Thread[Thread-39,5,main]]
返回的sign:1384f3a2-8d5e-43be-968e-2fc2d25d4826
number:0
isInterrupted:true
小于等于0 这个时候没了
isInterrupted:true
isInterrupted:true
isInterrupted:true
加锁keyMap:[lock=7e4dae38-2a4c-4ee3-a4b8-0439389eb793]
加锁threadMap:[lock=Thread[Thread-40,5,main]]
返回的sign:7e4dae38-2a4c-4ee3-a4b8-0439389eb793
number:0
isInterrupted:true
小于等于0 这个时候没了
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
加锁keyMap:[lock=9fce46b9-6b1e-4061-a6ad-9d3c0642b401]
加锁threadMap:[lock=Thread[Thread-41,5,main]]
返回的sign:9fce46b9-6b1e-4061-a6ad-9d3c0642b401
number:0
isInterrupted:true
小于等于0 这个时候没了
isInterrupted:true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值