Redisson分布式锁:tryLock的源码解析重试机制以及其看门狗原理

中间有些可能没有排版好,如果需要可以找我要彩色的PDF版

tryLock的源码解析重试机制以及其看门狗原理

编写一个测试类:

@Slf4j
@SpringBootTest
public class RedissonTest {
    @Autowired
    private RedissonClient redissonClient;
    private RLock lock;

    @BeforeEach
    void setUp() {
        lock = redissonClient.getLock("order"); //创建锁对象
    }

    @Test
    void method1() throws InterruptedException {
        //尝试获取锁
        boolean isLock = lock.tryLock(1L, TimeUnit.SECONDS);
        if(!isLock){
            log.error("获取锁失败…………1");
            return;
        }
        try {
            log.info("获取锁成功…………1"); //获取锁成功
            method2();
        } finally {
            log.info("释放锁…………1");
            lock.unlock();
        }
    }
    void method2() {
        boolean isLock = lock.tryLock();
        if(!isLock){
            log.error("获取锁失败…………2");
            return;
        }
        try {
           
            log.info("获取锁成功…………2"); //获取锁成功
        } finally {
            log.info("释放锁…………2");
            lock.unlock();
        }
    }
}
tryLock方法:

可传入参数有三种:

  1. long waitTime,long leaseTime,TimeUnit unit:可重试锁

    1. long waitTime获取锁的最大等待时长:如果设定,在第一次没有获取到锁之后,不是立刻返回,而是在最大等待时间内不断尝试,如果等待时间结束了,还没有获取到锁,则返回false

    2. long leaseTime锁自动释放的时间,过期时间

    3. TimeUnit unit时间的单位

  2. <no parameters>:无参数

  3. long time,@NotNull TimeUnit unit

    1. long time:获取锁的最大等待时长

    2. TimeUnit unit:时间的单位

  • 可以传入等待时间

  • 不传入等待时间:默认是看门狗机制,30秒

为了研究锁重试机制,所以等待传入等待时间:lock.tryLock(1L, TimeUnit.SECONDS);

进入tryLock
public boolean tryLock(long waitTime, TimeUnit unit) throws InterruptedException {
    return this.tryLock(waitTime, -1L, unit);
}

leaseTime没有传,给了默认值-1

进入tryLock
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(); //得到线程的Id,用于后续的锁里的标识
    Long ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId); //tryAcquire:尝试获取锁
    if (ttl == null) {
        return true;
    } else {
        time -= System.currentTimeMillis() - current;
        if (time <= 0L) {
            this.acquireFailed(waitTime, unit, threadId);
            return false;
        } else {
            current = System.currentTimeMillis();
            RFuture<RedissonLockEntrysubscribeFuture = this.subscribe(threadId);
            if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
                if (!subscribeFuture.cancel(false)) {
                    subscribeFuture.onComplete((res, e) -> {
                        if (e == null) {
                            this.unsubscribe(subscribeFuture, threadId);
                        }

                    });
                }

                this.acquireFailed(waitTime, unit, threadId);
                return false;
            } else {
                boolean var14;
                try {
                    time -= System.currentTimeMillis() - current;
                    if (time > 0L) {
                        boolean var16;
                        do {
                            long currentTime = System.currentTimeMillis();
                            ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);
                            if (ttl == null) {
                                var16 = true;
                                return var16;
                            }

                            time -= System.currentTimeMillis() - currentTime;
                            if (time <= 0L) {
                                this.acquireFailed(waitTime, unit, threadId);
                                var16 = false;
                                return var16;
                            }

                            currentTime = System.currentTimeMillis();
                            if (ttl >= 0L && ttl < time) {
                                ((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                            } else {
                                ((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
                            }

                            time -= System.currentTimeMillis() - currentTime;
                        } while(time > 0L);

                        this.acquireFailed(waitTime, unit, threadId);
                        var16 = false;
                        return var16;
                    }

                    this.acquireFailed(waitTime, unit, threadId);
                    var14 = false;
                } finally {
                    this.unsubscribe(subscribeFuture, threadId);
                }

                return var14;
            }
        }
    }
}
进入tryAcquire方法获取锁的业务逻辑
private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    return (Long)this.get(this.tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}

tryAcquire方法里面调用了tryAcquireAsync

进入tryAcquireAsync方法
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    if (leaseTime != -1L) { //先判断释放时间是否是-1
        return this.tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
    } else {
        RFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(waitTime, 
                this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), 
                TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
        ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
            if (e == null) {
                if (ttlRemaining == null) {
                    this.scheduleExpirationRenewal(threadId);
                }
            }
        });
        return ttlRemainingFuture;
    }
}

先判断释放时间是否是-1,没有传,默认-1;

传没传入释放时间都要走tryLockInnerAsync方法,

  只不过没传的话,会给你默认值:this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout()

getLockWatchdogTimeout:看门狗的超时时间

进入getLockWatchdogTimeout中可以看到,看门狗的超时时间为30s

接着进入tryLockInnerAsync方法中
 <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
         this.internalLockLeaseTime = unit.toMillis(leaseTime);//释放时间记录到本地的一个成员表量中
         
         return this.evalWriteAsync(this.getName(), LongCodec.INSTANCE, command,
             "if (redis.call('exists', KEYS[1]) == 0) then " + //判断锁是否存在
             "redis.call('hset', KEYS[1], ARGV[2], 1); " +     //写数据(hash结构)
             "redis.call('pexpire', KEYS[1], ARGV[1]); " +     //设置过期时间
             "return nil; " +
             "end; " +
             "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " + //判断锁是否是属于自己
             "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +   //锁的value进行+1
             "redis.call('pexpire', KEYS[1], ARGV[1]); " +      //设置过期时间
             "return nil; " +
             "end; " +
             "return redis.call('pttl', KEYS[1]);"    //抢锁失败,返回当前这把锁的失效时间
             , Collections.singletonList(this.getName()), //KEY集合
             this.internalLockLeaseTime,   //ARGV[1]
             this.getLockName(threadId));  //ARGV[2]
 }

这个方法就是获取锁的源码

  • this.internalLockLeaseTime = unit.toMillis(leaseTime);:将锁的释放时间记录到了本地的一个成员表量中internalLockLeaseTime :内部的锁的释放时间

  • Lua脚本获取锁逻辑

    • 判断锁是否存在

      1. 不存在

        • 记录锁的标识 并且 次数+1

        • 设置过期时间

        • 返回 nil 获取锁成功

      2. 存在

        • 判断锁的标识是不是自己的

          1. 是自己的

            1. 锁的次数+1

            2. 设置过期时间

            3. 返回 nil 获取锁成功

          2. 不是自己的

            1. 返回当前这把锁的剩余失效时间 获取锁失败

返回锁的剩余失效时间有什么用呢?

重试机制

1.把锁的剩余失效时间返回到tryLockInnerAsync的上一个方法
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    if (leaseTime != -1L) { //先判断释放时间是否是-1
        return this.tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
    } else {
        RFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(waitTime, 
                this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), 
                TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
        ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
            if (e == null) {
                if (ttlRemaining == null) {
                    this.scheduleExpirationRenewal(threadId);
                }
            }
        });
        return ttlRemainingFuture;
    }
}

tryLockInnerAsync的方法结果封装到了RFuture<Long> ttlRemainingFuture(在RFuture中)

因为tryLockInnerAsync函数是一个异步函数,意思是函数执行完了,代表命令发出去了,结果那没拿到,还不清楚,所以封装到了RFuture中

最后也是返回的RFuture<Long> ttlRemainingFuture

2.把RFuture返回到tryAcquireAsync的上一个方法
private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    return (Long)this.get(this.tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}

这里的get就获得到了阻塞等待RFuture的结果——>等待里面的剩余有效期

3.把等待里面的剩余有效期返回到tryAcquire的上一个方法
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(); //得到线程的Id,用于后续的锁里的标识
    Long ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId); //tryAcquire:尝试获取锁
    if (ttl == null) {
        return true;
    } else {
        time -= System.currentTimeMillis() - current;
        if (time <= 0L) {
            this.acquireFailed(waitTime, unit, threadId);
            return false;
        } else { //开始重试
            current = System.currentTimeMillis();
            RFuture<RedissonLockEntrysubscribeFuture = this.subscribe(threadId);//等待订阅消息
            if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) { //判断是否超时
                if (!subscribeFuture.cancel(false)) {
                    subscribeFuture.onComplete((res, e) -> {
                        if (e == null) {
                            this.unsubscribe(subscribeFuture, threadId);
                        }
                    });
                }                                                    

                this.acquireFailed(waitTime, unit, threadId);
                return false;
            } else {    //订阅没有超时
                boolean var14;
                try {
                    time -= System.currentTimeMillis() - current;
                    if (time > 0L) {
                        boolean var16;
                        do {
                            long currentTime = System.currentTimeMillis();
                            ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);
                            if (ttl == null) {
                                var16 = true;
                                return var16;
                            }

                            time -= System.currentTimeMillis() - currentTime;
                            if (time <= 0L) {
                                this.acquireFailed(waitTime, unit, threadId);
                                var16 = false;
                                return var16;
                            }

                            currentTime = System.currentTimeMillis();
                            if (ttl >= 0L && ttl < time) {
                                ((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                            } else {
                                ((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
                            }

                            time -= System.currentTimeMillis() - currentTime;
                        } while(time > 0L);

                        this.acquireFailed(waitTime, unit, threadId);
                        var16 = false;
                        return var16;
                    }

                    this.acquireFailed(waitTime, unit, threadId);
                    var14 = false;
                } finally {
                    this.unsubscribe(subscribeFuture, threadId);
                }

                return var14;
            }
        }
    }
}

  • Long ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);这里经历了获取锁的逻辑,得到的结果要么是nil(成功获取到锁);要么是锁的剩余等待时间(获取锁失败)

  • 为null获取锁成功,直接返回

  • 不为null,则得到锁剩余等待时间,获取锁失败,准备重试获取锁

    • time -= System.currentTimeMillis() - current;:当前时间-才准备获取锁时的时间=获取锁过程消耗的时间,然后再用最大等待时间time=time - 获取锁过程消耗的时间此时time就是剩余的等待时间

      long time = unit.toMillis(waitTime);  //等待时间传为毫秒,后续都是以毫秒来计算long current = System.currentTimeMillis(); //获取到当前时间
      long threadId = Thread.currentThread().getId(); //得到线程的Id,用于后续的锁里的标识
      Long ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId); //tryAcquire:尝试获取锁
      if (ttl == null) {
          return true;
      } else {
          time -= System.currentTimeMillis() - current;
    • 判断剩余的等待时间是否>0:防止获取锁时间太长,直接超过了最大等待时间

      1. <=0:获取锁失败,this.acquireFailed(waitTime, unit, threadId); return false;

      2. >0:还有剩余的等待时间,开始尝试

         current = System.currentTimeMillis();//获取准备开始尝试的当前的时间
        RFuture<RedissonLockEntrysubscribeFuture = this.subscribe(threadId);//不会立刻尝试,订阅释放锁的消息
        if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) { //尝试等待
            if (!subscribeFuture.cancel(false)) {
                subscribeFuture.onComplete((res, e) -> {
                    if (e == null) {
                        this.unsubscribe(subscribeFuture, threadId);
                    }
                });
            }
            this.acquireFailed(waitTime, unit, threadId);
            return false;

        • current = System.currentTimeMillis();获取准备开始尝试的当前的时间

        • 不会立即尝试,因为上一秒才获取锁失败,立马尝试获取锁,很大概率也是失败,这样会增加CPU的负担,而是采取订阅机制

          在释放锁的lua脚本中 ,会"redis.call('publish', KEYS[2], ARGV[1]); " 发布一条消息,通知大家:锁释放了

          1. this.subscribe(threadId);:订阅释放锁的消息,因为收到释放锁的消息的时间是不确定的,所以这里的结果也是封装到了RFuture类型

        • !subscribeFuture.await(time, TimeUnit.MILLISECONDS)调用futureawait方法尝试等待,当且仅当future指定的时间限制内(time:剩余的等待时间)完成时为True

          1. 没有完成,返回为false,!false就是true,

            1. 订阅超时

              1. this.unsubscribe(subscribeFuture, threadId);订阅超时,取消订阅,返回false

            2. 订阅没有超时

               

              } else { //订阅没有超时 boolean var14; try { time -= System.currentTimeMillis() - current; //得到剩余最大等待时间 if (time > 0L) { boolean var16; do { //开始重试获取锁 long currentTime = System.currentTimeMillis(); ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId); if (ttl == null) { var16 = true; //获取锁成功 return var16; //true } time -= System.currentTimeMillis() - currentTime; if (time <= 0L) { this.acquireFailed(waitTime, unit, threadId); //等待时间<=0 var16 = false; return var16; //false } //继续重试 currentTime = System.currentTimeMillis(); if (ttl >= 0L && ttl < time) { ((RedissonLockEntry)subscribeFuture.getNow()) .getLatch() .tryAcquire(ttl, TimeUnit.MILLISECONDS); } else { ((RedissonLockEntry)subscribeFuture.getNow()) .getLatch() .tryAcquire(time, TimeUnit.MILLISECONDS); } time -= System.currentTimeMillis() - currentTime; } while(time > 0L); this.acquireFailed(waitTime, unit, threadId); var16 = false; return var16; } this.acquireFailed(waitTime, unit, threadId); var14 = false; } finally { this.unsubscribe(subscribeFuture, threadId); } return var14; }

              1. time -= System.currentTimeMillis() - current; 得到剩余最大等待时间

              2. 判断剩余时间是否>0

                1. 不>0: this.acquireFailed(waitTime, unit, threadId); var14 = false;

                2. 最大等待时间>0:

                  1. ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);重试获取锁

                  2. 判断是否获取锁成功

                    1. ttl == null获取锁成功,var16 = true;return var16;返回var16(true)

                    2. 获取锁失败

                3. while(time > 0L);判断最大等待时间是否>0

                  1. >0: 继续尝试,循环步骤b分布式锁-Redis&Redission

                  2. <=0:this.acquireFailed(waitTime, unit, threadId);var14 = false;将var14设为false,代表结束

                4. 进入finally: this.unsubscribe(subscribeFuture, threadId);取消订阅

                5. return var14;;如果执行到这里,则代表等待时间耗尽了,仍没有获取到锁,返回false

巧妙之处,用的是利用的是消息订阅和信号量的这种机制,不是无休止的忙等机制(while(true)),而是等他释放了锁,再去尝试,对CPU比较友好

看门狗机制

怎么确保业务时执行完释放,而不是因为阻塞ttl(剩余过期时间)释放呢?

前提:没有传入leaseTime锁自动释放的时间,过期时间 ,即leaseTime=-1

进入tryAcquire
private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    return (Long)this.get(this.tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}
进入tryAcquireAsync
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    if (leaseTime != -1L) {
        return this.tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
    } else {
        RFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(waitTime, 
                this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), 
                TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
        ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
            if (e == null) {
                if (ttlRemaining == null) {
                    this.scheduleExpirationRenewal(threadId);
                }
            }
        });
        return ttlRemainingFuture;
    }
}
  • lessTime=-1的时候,tryLockInnerAsync方法,其中会开启看门狗机制,方法返回值得到furure

  • ttlRemainingFuture.onComplete,当future完成后,有点像回调函数:当tryLockInnerAsync函数回调成功后,执行onComplete中的代码

    ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
                if (e == null) {
                    if (ttlRemaining == null) {
                        this.scheduleExpirationRenewal(threadId);
                    }
                }
            });
    • ttlRemaining剩余有效期,ttlRemaining==null获取锁成功

    • 获取锁成功之后,需要解决有效期的问题scheduleExpirationRenewal(自动更新续期)

进入scheduleExpirationRenewal
private void scheduleExpirationRenewal(long threadId) {
    RedissonLock.ExpirationEntry entry = new RedissonLock.ExpirationEntry();
    RedissonLock.ExpirationEntry oldEntry = 
                (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP
                    .putIfAbsent(this.getEntryName(), entry);
    if (oldEntry != null) {
        oldEntry.addThreadId(threadId);
    } else {
        entry.addThreadId(threadId);
        this.renewExpiration();
    }

}
  1. 创建了一个ExpirationEntry类型的对象entry

  2. 放入EXPIRATION_RENEWAL_MAP中,key是String类型;value是ExpirationEntry类型

    private static final ConcurrentMap<String, RedissonLock.ExpirationEntry> EXPIRATION_RENEWAL_MAP = new ConcurrentHashMap()

  3. EXPIRATION_RENEWAL_MAP.putIfAbsent(this.getEntryName(), entry);

    1. getEntryName :可以理解为当前锁的名称

    2. EXPIRATION_RENEWAL_MAP是静态的,可以说RedissonLock这个类的所有实例,都可以看成这个Map,而一个Lock类将来会创建多个不同锁的实例,因为不同的业务会创建不同的锁,每一个锁都会有自己的名字,因此他们在map里面都会有唯一的key和自己唯一的entry;一个锁对应一个entry,不重复;

    3. 当第一次来的时候,这个entry一定不存在,所以用putIfAbsent,如果不存在,就往里面放一个新的entry,这时候返回值就是null

    4. 如果不是第一次,是重入的,第二次来putIfAbsent就返回旧的entry(第一个线程来的时候创建的entey);所以不管重入几次,将来拿到的都是同一个entry,保证同一个锁永远拿到的是同一个entry

    5. oldEntry != null第一次以后来的线程,获得到的entry都不为null,所以直接加进来oldEntry.addThreadId(threadId);,其实不同的线程一定是不可能拿到同一把锁的,所以第一次以后来的线程一定还是第一次的那一线程(同一线程),这是一种重入

    6. 不管是旧的还是新的,但是第一次来会多做一个this.renewExpiration();(更新有效期)方法:

    7. 所以为什么如果是oldEntry 的话就不用调用renewExpiration函数了,因为oldEntry 里面已经有了这个定时任务了,他已经在一直执行定时任务了,只不过把threadId传进去

    8. 所以为什么如果是oldEntry 的话就不用调用renewExpiration函数了,因为oldEntry 里面已经有了这个定时任务了,他已经在一直执行定时任务了,只不过把threadId传进去

    9. if (oldEntry != null) {
              oldEntry.addThreadId(threadId);
          } else {
              entry.addThreadId(threadId);
              this.renewExpiration();
          }

那什么时候才能释放呢?什么时候这个任务才会取消呢?

自然是在锁释放的时候

进入unlock
public void unlock() {
    try {
        this.get(this.unlockAsync(Thread.currentThread().getId()));
    } catch (RedisException var2) {
        if (var2.getCause() instanceof IllegalMonitorStateException) {
            throw (IllegalMonitorStateException)var2.getCause();
        } else {
            throw var2;
        }
    }
}

进入unlockAsync释放锁,清除定时任务
public RFuture<Void> unlockAsync(long threadId) {
    RPromise<Void> result = new RedissonPromise();
    RFuture<Boolean> future = this.unlockInnerAsync(threadId);
    future.onComplete((opStatus, e) -> {
        this.cancelExpirationRenewal(threadId);
        if (e != null) {
            result.tryFailure(e);
        } else if (opStatus == null) {
            IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: " + this.id + " thread-id: " + threadId);
            result.tryFailure(cause);
        } else {
            result.trySuccess((Object)null);
        }
    });
    return result;
}
  1. RFuture<Boolean> future = this.unlockInnerAsync(threadId);执行unlockInnerAsync函数返回值为future

  2. future.onComplete执行cancelExpirationRenewal函数

    void cancelExpirationRenewal(Long threadId) {
        RedissonLock.ExpirationEntry task = 
                (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP
                        .get(this.getEntryName());
        if (task != null) {
            if (threadId != null) {
                task.removeThreadId(threadId);
            }
            if (threadId == null || task.hasNoThreads()) {
                Timeout timeout = task.getTimeout();
                if (timeout != null) {
                    timeout.cancel();
                }
                EXPIRATION_RENEWAL_MAP.remove(this.getEntryName());
            }
        }
    }
    1. 先从map中取出当前这把锁的定时任务,map是静态的,根据锁的名称来取,每个锁有自己的定时任务

    2. task.removeThreadId(threadId);threadId移除

    3. Timeout timeout = task.getTimeout();取出Timeout任务,然后timeout.cancel();把任务取消掉

    4. EXPIRATION_RENEWAL_MAP.remove(this.getEntryName());把entry移除

总结

Redisson分布式锁原理:

  • 可重入:利用hash结构记录线程id和重入次数

  • 可重试:利用信号量PubSub功能实现等待、唤醒,获取锁失败的重试机制

  • 超时续约:利用watchDog,每隔一段时间(releaseTime/3),重置超时时间

  • 32
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值