spring整合的redis分布式锁

本文详细描述了如何在Spring框架中使用Redis实现分布式锁,包括依赖引入、RedisLockRegistry的注入和使用,以及obtain和tryLock方法的工作原理。重点介绍了加锁和解锁的lua脚本执行过程。
摘要由CSDN通过智能技术生成

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
    评论
Spring Boot中,可以通过使用Redis实现分布式锁。以下是一种可能的实现方式: 1. 首先,在项目的`pom.xml`文件中添加对spring-boot-starter-data-redis的依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 在`application.properties`文件中配置Redis的连接信息。 ```properties spring.redis.host=127.0.0.1 spring.redis.port=6379 ``` 3. 创建一个`RedisLock`类,用于获取和释放分布式锁。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @Component public class RedisLock { @Autowired private RedisTemplate<String, String> redisTemplate; public boolean acquireLock(String key, String value, long timeout) { return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS); } public void releaseLock(String key, String value) { String currentValue = redisTemplate.opsForValue().get(key); if (currentValue != null && currentValue.equals(value)) { redisTemplate.delete(key); } } } ``` 4. 在需要使用分布式锁的地方,注入`RedisLock`并使用它进行加锁和解锁操作。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class YourService { @Autowired private RedisLock redisLock; public void doSomething() { String lockKey = "your-lock-key"; String requestId = UUID.randomUUID().toString(); try { boolean locked = redisLock.acquireLock(lockKey, requestId, 5000); if (locked) { // 获取到锁,执行业务逻辑 // ... } else { // 未获取到锁,处理失败逻辑 // ... } } finally { redisLock.releaseLock(lockKey, requestId); } } } ``` 上述代码中,`acquireLock`方法用于尝试获取分布式锁,它使用Redis的`setIfAbsent`方法来设置一个键值对,如果键不存在则设置成功,返回`true`表示获取到了锁。`releaseLock`方法用于释放锁,它首先比较当前锁的值是否与传入的值相等,如果相等则删除该键。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值