• 技术文章 >数据库 >Redis

    什么是可重入锁?详解redis实现分布式重入锁的方法

    青灯夜游青灯夜游2022-02-17 10:47:38转载305
    什么是可重入锁?怎么实现重入锁?下面本篇文章就来带大家深入聊聊redis实现分布式重入锁的方法,希望对大家有所帮助!

    什么是不可重入锁?

    即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到而阻塞。

    什么是可重入锁?

    可重入锁,也叫做递归锁,指的是在同一线程内,外层函数获得锁之后,内层递归函数仍然可以获取到该锁。 就是同一个线程再次进入同样代码时,可以再次拿到该锁。

    可重入锁作用?

    防止在同一线程中多次获取锁而导致死锁发生。

    注:在java的编程中synchronized 和 ReentrantLock都是可重入锁。

    基于synchronized的可重入锁

    步骤1:双重加锁逻辑

    public class SynchronizedDemo {
        //模拟库存100
        int count=100;
        public synchronized void operation(){
            log.info("第一层锁:减库存");
            //模拟减库存
            count--;
            add();
            log.info("下订单结束库存剩余:{}",count);
        }
    
        private synchronized void add(){
            log.info("第二层锁:插入订单");
            try {
                Thread.sleep(1000*10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    步骤2:加个测试类

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo=new SynchronizedDemo();
        for (int i = 0; i < 3; i++) {
            int finalI = i;
            new Thread(()->{
                log.info("-------用户{}开始下单--------", finalI);
                synchronizedDemo.operation();
            }).start();
        }
    }

    步骤3:测试

    20:44:04.013 [Thread-2] INFO com.agan.redis.controller.SynchronizedController - -------用户2开始下单--------
    20:44:04.013 [Thread-1] INFO com.agan.redis.controller.SynchronizedController - -------用户1开始下单--------
    20:44:04.013 [Thread-0] INFO com.agan.redis.controller.SynchronizedController - -------用户0开始下单--------
    20:44:04.016 [Thread-2] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第一层锁:减库存
    20:44:04.016 [Thread-2] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第二层锁:插入订单
    20:44:14.017 [Thread-2] INFO com.agan.redis.Reentrant.SynchronizedDemo - 下订单结束库存剩余:99
    20:44:14.017 [Thread-0] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第一层锁:减库存
    20:44:14.017 [Thread-0] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第二层锁:插入订单
    20:44:24.017 [Thread-0] INFO com.agan.redis.Reentrant.SynchronizedDemo - 下订单结束库存剩余:98
    20:44:24.017 [Thread-1] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第一层锁:减库存
    20:44:24.017 [Thread-1] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第二层锁:插入订单
    20:44:34.017 [Thread-1] INFO com.agan.redis.Reentrant.SynchronizedDemo - 下订单结束库存剩余:97

    基于ReentrantLock的可重入锁

    ReentrantLock,是一个可重入且独占式的锁,是一种递归无阻塞的同步锁。和synchronized关键字相比,它更灵活、更强大,增加了轮询、超时、中断等高级功能。

    步骤1:双重加锁逻辑

    public class ReentrantLockDemo {
    
        private Lock lock =  new ReentrantLock();
    
        public void doSomething(int n){
            try{
                //进入递归第一件事:加锁
                lock.lock();
                log.info("--------递归{}次--------",n);
                if(n<=2){
                    try {
                        Thread.sleep(1000*2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    this.doSomething(++n);
                }else{
                    return;
                }
            }finally {
                lock.unlock();
            }
        }
    
    }

    步骤2:加个测试类

    public static void main(String[] args) {
        ReentrantLockDemo reentrantLockDemo=new ReentrantLockDemo();
        for (int i = 0; i < 3; i++) {
            int finalI = i;
            new Thread(()->{
                log.info("-------用户{}开始下单--------", finalI);
                reentrantLockDemo.doSomething(1);
            }).start();
        }
    }

    步骤3:测试

    20:55:23.533 [Thread-1] INFO com.agan.redis.controller.ReentrantController - -------用户1开始下单--------
    20:55:23.533 [Thread-2] INFO com.agan.redis.controller.ReentrantController - -------用户2开始下单--------
    20:55:23.533 [Thread-0] INFO com.agan.redis.controller.ReentrantController - -------用户0开始下单--------
    20:55:23.536 [Thread-1] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------递归1次--------
    20:55:25.537 [Thread-1] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------递归2次--------
    20:55:27.538 [Thread-1] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------递归3次--------
    20:55:27.538 [Thread-2] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------递归1次--------
    20:55:29.538 [Thread-2] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------递归2次--------
    20:55:31.539 [Thread-2] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------递归3次--------
    20:55:31.539 [Thread-0] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------递归1次--------
    20:55:33.539 [Thread-0] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------递归2次--------
    20:55:35.540 [Thread-0] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------递归3次--------

    redis如何实现分布式重入锁?

    setnx虽然可以实现分布式锁,但是不可重入,在一些复杂的业务场景,我们需要分布式重入锁时, 对于redis的重入锁业界还是有很多解决方案的,目前最流行的就是采用Redisson。【相关推荐:Redis视频教程

    什么是Redisson?

    案例实战:体验redis分布式重入锁

    步骤1:Redisson配置

    Redisson配置的可以查考:redis分布式缓存(三十四)一一 SpringBoot整合Redission - 掘金 (juejin.cn)

    https://juejin.cn/post/7057132897819426824

    步骤2:Redisson重入锁测试类

    public class RedisController {
    
        @Autowired
        RedissonClient redissonClient;
    
        @GetMapping(value = "/lock")
        public void get(String key) throws InterruptedException {
            this.getLock(key, 1);
        }
    
        private void getLock(String key, int n) throws InterruptedException {
            //模拟递归,3次递归后退出
            if (n > 3) {
                return;
            }
            //步骤1:获取一个分布式可重入锁RLock
            //分布式可重入锁RLock :实现了java.util.concurrent.locks.Lock接口,同时还支持自动过期解锁。
            RLock lock = redissonClient.getLock(key);
            //步骤2:尝试拿锁
            // 1. 默认的拿锁
            //lock.tryLock();
            // 2. 支持过期解锁功能,10秒钟以后过期自动解锁, 无需调用unlock方法手动解锁
            //lock.tryLock(10, TimeUnit.SECONDS);
            // 3. 尝试加锁,最多等待3秒,上锁以后10秒后过期自动解锁
            // lock.tryLock(3, 10, TimeUnit.SECONDS);
            boolean bs = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if (bs) {
                try {
                    // 业务代码
                    log.info("线程{}业务逻辑处理: {},递归{}" ,Thread.currentThread().getName(), key,n);
                    //模拟处理业务
                    Thread.sleep(1000 * 5);
                    //模拟进入递归
                    this.getLock(key, ++n);
                } catch (Exception e) {
                    log.error(e.getLocalizedMessage());
                } finally {
                    //步骤3:解锁
                    lock.unlock();
                    log.info("线程{}解锁退出",Thread.currentThread().getName());
                }
            } else {
                log.info("线程{}未取得锁",Thread.currentThread().getName());
            }
        }
    }

    RLock三个加锁动作:

    区别:

    最佳实战:

    步骤3:测试

    访问3次:http://127.0.0.1:9090/lock?key=ljw

    线程http-nio-9090-exec-1业务逻辑处理: ljw,递归1
    线程http-nio-9090-exec-2未取得锁
    线程http-nio-9090-exec-1业务逻辑处理: ljw,递归2
    线程http-nio-9090-exec-3未取得锁
    线程http-nio-9090-exec-1业务逻辑处理: ljw,递归3
    线程http-nio-9090-exec-1解锁退出
    线程http-nio-9090-exec-1解锁退出
    线程http-nio-9090-exec-1解锁退出

    通过测试结果:

    总结

    上面介绍了分布式重入锁的相关知识,证明了Redisson工具能实现了可重入锁的功能。其实Redisson工具包中还包含了读写锁(ReadWriteLock)和 红锁(RedLock)等相关功能,我们下篇文章再详细研究。

    更多编程相关知识,请访问:编程入门!!

    以上就是什么是可重入锁?详解redis实现分布式重入锁的方法的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除
    上一篇:Redis源码解析之深入了解Makefile文件 下一篇:Redis学习之深入了解Bitmaps
    PHP编程就业班

    相关文章推荐

    • 深入解析Redis中的数据结构,聊聊应用场景• 解析Redis中的哨兵模式,聊聊搭建和执行流程• 浅析Redis中的锁,聊聊Redlock(redis分布式锁)• 浅析Redis中的哨兵模式原理• 保证mysql和redis的双写一致性• 浅析Redis6中的单线程和多线程模型

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网