源码阅读:Redission锁

74 阅读3分钟

新开一个源码阅读系列,记录下自己阅读的源码

参考# 黑马程序员Redis入门到实战教程-P67等多个视频

数据结构

Hash结构 image.png Value记录同一个锁的加锁次数,以满足可重入需求

可重入机制

  1. 加锁org.redisson.RedissonLock#tryLockInnerAsync
"if (redis.call('exists', KEYS[1]) == 0) then " +               key不存在
"redis.call('hset', KEYS[1], ARGV[2], 1); " +  
"redis.call('pexpire', KEYS[1], ARGV[1]); " +  
"return nil; " +  
"end; " +  
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +     key存在
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +                value++
"redis.call('pexpire', KEYS[1], ARGV[1]); " +                   续期
"return nil; " +  
"end; " +  
"return redis.call('pttl', KEYS[1]);",                          加锁失败:返回key的剩余时间
  1. 解锁org.redisson.RedissonLock#unlockInnerAsync
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +           key不存在
"return nil;" +  
"end; " +  
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +     value--
"if (counter > 0) then " +                                            存在重入,延时
"redis.call('pexpire', KEYS[1], ARGV[2]); " +                         
"return 0; " +  
"else " +  
"redis.call('del', KEYS[1]); " +                                      不存在重入,删锁
"redis.call('publish', KEYS[2], ARGV[1]); " +                         发布消息
"return 1; "+  
"end; " +  
"return nil;",

重试机制

分析代码org.redisson.RedissonLock#tryLock

  1. 尝试加锁,若加锁成功结束
  2. 加锁失败 && waitTime > 0 则订阅释放锁消息
  3. 收到释放锁消息 && waitTime > 0 则再次尝试加锁
  4. 加锁失败 && waitTime > 0 则等待信号量
    @Override
    public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
        long time = unit.toMillis(waitTime);
        long current = System.currentTimeMillis();
        long threadId = Thread.currentThread().getId();
        Long ttl = tryAcquire(leaseTime, unit, threadId);
        // lock acquired
        if (ttl == null) {
            return true;
        }
        
        // 判断订阅时间
        time -= System.currentTimeMillis() - current;
        if (time <= 0) {
            acquireFailed(threadId);
            return false;
        }
        
        // 订阅释放锁消息
        current = System.currentTimeMillis();
        RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
        if (!await(subscribeFuture, time, TimeUnit.MILLISECONDS)) {
            if (!subscribeFuture.cancel(false)) {
                subscribeFuture.onComplete((res, e) -> {
                    if (e == null) {
                        unsubscribe(subscribeFuture, threadId);   // 取消订阅
                    }
                });
            }
            acquireFailed(threadId);
            return false;
        }
        
        
        // 尝试释放锁
        try {
            time -= System.currentTimeMillis() - current;
            if (time <= 0) {
                acquireFailed(threadId);
                return false;
            }
        
            while (true) {
                long currentTime = System.currentTimeMillis();
                ttl = tryAcquire(leaseTime, unit, threadId);
                // lock acquired
                if (ttl == null) {
                    return true;
                }

                time -= System.currentTimeMillis() - currentTime;
                if (time <= 0) {
                    acquireFailed(threadId);
                    return false;
                }

                // waiting for message  通过信号量等待
                currentTime = System.currentTimeMillis();
                if (ttl >= 0 && ttl < time) {  // 找最小时间进行等待
                    getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                } else {
                    getEntry(threadId).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
                }

                time -= System.currentTimeMillis() - currentTime;
                if (time <= 0) {
                    acquireFailed(threadId);
                    return false;
                }
            }
        } finally {
            unsubscribe(subscribeFuture, threadId);
        }
//        return get(tryLockAsync(waitTime, leaseTime, unit));
    }

续约&&释放锁

  1. 续约通过看门狗实现

谁续约:拿到锁的线程续约

核心数据结构: org.redisson.RedissonLock#EXPIRATION_RENEWAL_MAP(private static final ConcurrentMap<String, ExpirationEntry>)

key是entryName即 Redission线程id+锁名称;value是ExpirationEntry

核心方法是org.redisson.RedissonLock#renewExpiration,不停递归执行

    private void renewExpiration() {
        ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
        if (ee == null) {
            return;
        }
        
        Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
            @Override
            public void run(Timeout timeout) throws Exception {
                ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
                if (ent == null) {
                    return;
                }
                Long threadId = ent.getFirstThreadId();
                if (threadId == null) {
                    return;
                }
                
                RFuture<Boolean> future = renewExpirationAsync(threadId);
                future.onComplete((res, e) -> {
                    if (e != null) {
                        log.error("Can't update lock " + getName() + " expiration", e);
                        return;
                    }
                    
                    if (res) {
                        // reschedule itself  递归
                        renewExpiration();
                    }
                });
            }
        }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);   // 默认10秒执行
        
        ee.setTimeout(task);
    }

上文获取锁的方法org.redisson.RedissonLock#tryAcquireAsync中有处理续约的scheduleExpirationRenewal方法

    private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId) {
        if (leaseTime != -1) {
            return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
        }
        RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
        ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
            if (e != null) {
                return;
            }

            // lock acquired
            if (ttlRemaining == null) {
                scheduleExpirationRenewal(threadId);   // 拿到锁的线程续约
            }
        });
        return ttlRemainingFuture;
    }
  1. 锁释放 org.redisson.RedissonLock#unlockAsync(long)
    public RFuture<Void> unlockAsync(long threadId) {
        RPromise<Void> result = new RedissonPromise<Void>();
        RFuture<Boolean> future = unlockInnerAsync(threadId);

        future.onComplete((opStatus, e) -> {
            if (e != null) {
                cancelExpirationRenewal(threadId);
                result.tryFailure(e);
                return;
            }

            if (opStatus == null) {
                IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "
                        + id + " thread-id: " + threadId);
                result.tryFailure(cause);
                return;
            }
            
            cancelExpirationRenewal(threadId);
            result.trySuccess(null);
        });

        return result;
    }

org.redisson.RedissonLock#cancelExpirationRenewal中对任务进行取消

    void cancelExpirationRenewal(Long threadId) {
        ExpirationEntry task = EXPIRATION_RENEWAL_MAP.get(getEntryName());
        if (task == null) {
            return;
        }
        
        if (threadId != null) {
            task.removeThreadId(threadId);
        }
        
        if (threadId == null || task.hasNoThreads()) {
            task.getTimeout().cancel();                           // 取消任务
            EXPIRATION_RENEWAL_MAP.remove(getEntryName());        // 删除
        }
    }