spring整合的redis分布式锁

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

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

接下来看看这个redis分布式锁的实现原理,看下spring对细节的顶级处理。
首先obtain()方法就是从locks(Map<String, RedisLockRegistry.RedisLock>)中根据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);
        });
    }

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;
            }
        }
    }

获取锁的本质就是执行一段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;
        }

加锁的过程就是执行一段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);

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值