• spring整合的redis分布式锁


    spring整合了redis以后可以直接使用redis分布式锁,过程是:

    1. 引入依赖
        <dependency>
            <groupId>org.springframework.integrationgroupId>
            <artifactId>spring-integration-redisartifactId>
        dependency>
    
    • 1
    • 2
    • 3
    • 4
    1. 注入RedisConnectionFactory到RedisLockRegistry,这是用来连接redis执行redis执行的
        @Bean
        public RedisLockRegistry redisLockRegistry(RedisConnectionFactory connectionFactory){
            return new RedisLockRegistry(connectionFactory,LOCK_KEY,5000L);
        }
    
    • 1
    • 2
    • 3
    • 4
    1. 注入RedisLockRegistry到要用redis锁的地方,然后使用即可
        @Resource
        private RedisLockRegistry redisLockRegistry;
        //1.生成锁对象
        Lock lock = redisLockRegistry.obtain(lockKey);
        //2.获取锁
        if(lock.tryLock()){
            try{
                ......//3.业务代码
            }finally{
                //4.释放锁
                lock.unlock();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    接下来看看这个redis分布式锁的实现原理,看下spring对细节的顶级处理。
    首先obtain()方法就是从locks(Map)中根据key获取RedisLockRegistry.RedisLock对象,这里用来map进行缓存,对相同的key取得的就是相同的对象,避免重复new降低效率。

        public Lock obtain(Object lockKey) {
            Assert.isInstanceOf(String.class, lockKey);
            String path = (String)lockKey;
            return (Lock)this.locks.computeIfAbsent(path, (x$0) -> {
                return new RedisLockRegistry.RedisLock(x$0);
            });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    tryLock()方法就是获取redis分布式锁的。首先尝试获取本地ReentrantLock可重入公平锁,当应用的线程第一次获取redis锁时,就会在redis中生成key表示自己获取到锁了,如果接下来时应用本地的线程来获取锁,就会直接通过ReentrantLock先拿锁,没拿到说明有线程拿到锁了,就不用去redis获取,可以提高效率。然后再到redis去获取分布式锁

        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            long now = System.currentTimeMillis();
            //首先尝试获取本地ReentrantLock可重入公平锁
            if (!this.localLock.tryLock(time, unit)) {
                return false;
            } else {
                try {
                    long expire = now + TimeUnit.MILLISECONDS.convert(time, unit);
    
                    boolean acquired;
                    //不断获取直到超时
                    while(!(acquired = this.obtainLock()) && System.currentTimeMillis() < expire) {
                        Thread.sleep(100L);
                    }
                    //没拿到就释放本地ReentrantLock
                    if (!acquired) {
                        this.localLock.unlock();
                    }
    
                    return acquired;
                } catch (Exception var9) {
                    this.localLock.unlock();
                    this.rethrowAsLockException(var9);
                    return false;
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    获取锁的本质就是执行一段lua脚本加锁,首先锁key是在构造RedisLockRegistry.RedisLock生成的RedisLockRegistry.this.registryKey + ":" + path;,path就是我们要操作的redis中的key,

            private boolean obtainLock() {
                Boolean success = (Boolean)RedisLockRegistry.this.redisTemplate.execute(RedisLockRegistry.this.obtainLockScript, Collections.singletonList(this.lockKey), new Object[]{RedisLockRegistry.this.clientId, String.valueOf(RedisLockRegistry.this.expireAfter)});
                boolean result = Boolean.TRUE.equals(success);
                if (result) {
                    this.lockedAt = System.currentTimeMillis();
                }
    
                return result;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    加锁的过程就是执行一段lua脚本,先获取key的value,判断是否为自身的lockClientId,如果是说明我们已经拿到这个锁,为了保证可重入性,重新设置过期时间返回true继续加锁;如果key的value不存在说明没有应用线程拿到锁,我们自己来加锁,往redis中扔个key,value为自身lockClientId;否则说明这个锁已经被占用了,返回false。

    this.obtainLockScript = new DefaultRedisScript("
    local lockClientId = redis.call('GET', KEYS[1])
    if lockClientId == ARGV[1] then
        redis.call('PEXPIRE', KEYS[1], ARGV[2])
        return true
    elseif not lockClientId then
        redis.call('SET', KEYS[1], ARGV[1], 'PX', ARGV[2])
        return true
    end
    return false", Boolean.class);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    unlock就是释放锁了,就是到redis中删除key。

  • 相关阅读:
    终于可以一行代码也不用改了!ShardingSphere 原生驱动问世
    Oracle EBS Interface/API(44)- 销售订单发运明细行拆分
    A (1087) : DS单链表--类实现
    Linux日志信息
    2022年最新宁夏建筑安全员模拟题库及答案
    C++:变量:局部变量/成员变量/全局变量区别
    Shapiro-Francia正态检验
    数据库联查json解析后排序失败
    Java 18中简单 Web 服务器
    炒现货黄金怎么做?挖掘黄金的投资机会
  • 原文地址:https://blog.csdn.net/weixin_42145727/article/details/134340252