Redisson 分布式锁原理

1. 工具类

package com.meta.mall.common.utils;import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;/*** redisson 分布式工具类** @author gaoyang* @date 2022-05-14 08:58*/
@Slf4j
@Component
public class RedissonUtils {@Resourceprivate RedissonClient redissonClient;/*** 加锁** @param lockKey*/public void lock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);lock.lock();}/*** 带过期时间的锁** @param lockKey   key* @param leaseTime 上锁后自动释放锁时间*/public void lock(String lockKey, long leaseTime) {RLock lock = redissonClient.getLock(lockKey);lock.lock(leaseTime, TimeUnit.SECONDS);}/*** 带超时时间的锁** @param lockKey   key* @param leaseTime 上锁后自动释放锁时间* @param unit      时间单位*/public void lock(String lockKey, long leaseTime, TimeUnit unit) {RLock lock = redissonClient.getLock(lockKey);lock.lock(leaseTime, unit);}/*** 尝试获取锁** @param lockKey key* @return*/public boolean tryLock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);return lock.tryLock();}/*** 尝试获取锁** @param lockKey   key* @param waitTime  最多等待时间* @param leaseTime 上锁后自动释放锁时间* @return boolean*/public boolean tryLock(String lockKey, long waitTime, long leaseTime) {RLock lock = redissonClient.getLock(lockKey);try {return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);} catch (InterruptedException e) {log.error("RedissonUtils - tryLock异常", e);}return false;}/*** 尝试获取锁** @param lockKey   key* @param waitTime  最多等待时间* @param leaseTime 上锁后自动释放锁时间* @param unit      时间单位* @return boolean*/public boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {RLock lock = redissonClient.getLock(lockKey);try {return lock.tryLock(waitTime, leaseTime, unit);} catch (InterruptedException e) {log.error("RedissonUtils - tryLock异常", e);}return false;}/*** 释放锁** @param lockKey key*/public void unlock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);lock.unlock();}/*** 是否存在锁** @param lockKey key* @return*/public boolean isLocked(String lockKey) {RLock lock = redissonClient.getLock(lockKey);return lock.isLocked();}
}

2. lock和tryLock的区别

  1. 返回值
    lock 是 void;
    tryLock 是 boolean。

  2. 时机
    lock 一直等锁释放;
    tryLock 获取到锁返回true,获取不到锁并直接返回false。

lock拿不到锁会一直等待。tryLock是去尝试,拿不到就返回false,拿到返回true。
tryLock是可以被打断的,被中断的,lock是不可以。

3. 源码分析

3.1 lock

private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {// 获取当前线程 IDlong threadId = Thread.currentThread().getId();// 获取锁,正常获取锁则ttl为null,竞争锁时返回锁的过期时间Long ttl = tryAcquire(-1, leaseTime, unit, threadId);// lock acquiredif (ttl == null) {return;}// 订阅锁释放事件// 如果当前线程通过 Redis 的 channel 订阅锁的释放事件获取得知已经被释放,则会发消息通知待等待的线程进行竞争RFuture<RedissonLockEntry> future = subscribe(threadId);if (interruptibly) {commandExecutor.syncSubscriptionInterrupted(future);} else {commandExecutor.syncSubscription(future);}try {while (true) {// 循环重试获取锁,直至重新获取锁成功才跳出循环// 此种做法阻塞进程,一直处于等待锁手动释放或者超时才继续线程ttl = tryAcquire(-1, leaseTime, unit, threadId);// lock acquiredif (ttl == null) {break;}// waiting for messageif (ttl >= 0) {try {future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} catch (InterruptedException e) {if (interruptibly) {throw e;}future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);}} else {if (interruptibly) {future.getNow().getLatch().acquire();} else {future.getNow().getLatch().acquireUninterruptibly();}}}} finally {// 最后释放订阅事件unsubscribe(future, threadId);}
//        get(lockAsync(leaseTime, unit));}
    <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {return evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,"if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('hincrby', 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 " +"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"return redis.call('pttl', KEYS[1]);",Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));}

此段脚本为一段lua脚本:
KEY[1]: 为你加锁的lock值
ARGV[2]: 为线程id
ARGV[1]: 为设置的过期时间

第一个if:
判断是否存在设置lock的key是否存在,不存在则利用redis的hash结构设置一个hash,值为1,并设置过期时间,后续返回锁。
第二个if:
判断是否存在设置lock的key是否存在,存在此线程的hash,则为这个锁的重入次数加1(将hash值+1),并重新设置过期时间,后续返回锁。
最后返回:
这个最后返回不是说最后结果返回,是代表以上两个if都没有进入,则代表处于竞争锁的情况,后续返回竞争锁的过期时间。

3.2 tryLock

tryLock具有返回值,true或者false,表示是否成功获取锁。tryLock前期获取锁逻辑基本与lock一致,主要是后续获取锁失败的处理逻辑与lock不一致。

    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(waitTime, leaseTime, unit, threadId);// lock acquiredif (ttl == null) {return true;}// 获取锁失败后,中途tryLock会一直判断中间操作耗时是否已经消耗锁的过期时间,如果消耗完则返回falsetime -= System.currentTimeMillis() - current;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}current = System.currentTimeMillis();// 订阅锁释放事件// 如果当前线程通过 Redis 的 channel 订阅锁的释放事件获取得知已经被释放,则会发消息通知待等待的线程进行竞争.RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);// 将订阅阻塞,阻塞时间设置为我们调用tryLock设置的最大等待时间,超过时间则返回falseif (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {if (!subscribeFuture.cancel(false)) {subscribeFuture.onComplete((res, e) -> {if (e == null) {unsubscribe(subscribeFuture, threadId);}});}acquireFailed(waitTime, unit, threadId);return false;}try {time -= System.currentTimeMillis() - current;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}// 循环获取锁,但由于上面有最大等待时间限制,基本会在上面返回falsewhile (true) {long currentTime = System.currentTimeMillis();ttl = tryAcquire(waitTime, leaseTime, unit, threadId);// lock acquiredif (ttl == null) {return true;}time -= System.currentTimeMillis() - currentTime;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}// waiting for messagecurrentTime = System.currentTimeMillis();if (ttl >= 0 && ttl < time) {subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} else {subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);}time -= System.currentTimeMillis() - currentTime;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}}} finally {unsubscribe(subscribeFuture, threadId);}
//        return get(tryLockAsync(waitTime, leaseTime, unit));}

应尽量使用tryLock,且携带参数,因为可设置最大等待时间以及可及时获取加锁返回值,后续可做一些其他加锁失败的业务

Redis - Redisson lock和tryLock原理解析相关推荐

  1. redisson lock、tryLock分布式锁原理解析

    近期在处理程序有两个不同来源入口的时候,因为容易产生并发情况,造成会有脏数据产生,在同事推荐下使用redisson的锁来解决并发问题. 先上使用的一定程度封装的工具类: 工具类 @Service pu ...

  2. 超详细的Redisson实现分布式锁原理解析

    for update select column from table where column = ... for update 在select的sql上加上for update会对此记录加上行级锁 ...

  3. Redis进阶- Redisson分布式锁实现原理及源码解析

    文章目录 Pre 用法 Redisson分布式锁实现原理 Redisson分布式锁源码分析 redisson.getLock(lockKey) 的逻辑 redissonLock.lock()的逻辑 r ...

  4. Redisson之lock()和tryLock()的区别

    Redisson之lock()和tryLock()的区别和原理解析 在Redisson中 lock() 方法 与 tryLock() 方法是有区别的! 我们先来阐述两者的区别,再分析它们的源码. lo ...

  5. Redis分布式锁原理解析

    这章节我们来学习一下,Redis分布式锁的一个原理,首先我们看一下目录,最开始我们要讲一下,Redis分布式锁,相关的一些命令,然后在分布式锁演进的时候呢,还会以时间戳进行一个结合,后边还会讲一下,R ...

  6. java lock unlock_【Java并发007】原理层面:ReentrantLock中lock()、unlock()全解析

    一.前言 Java线程同步两种方式,synchronized关键字和Lock锁机制,其中,AQS队列就是Lock锁实现公平加锁的底层支持. 二.AQS源码对于lock.lock()的实现 2.1 AQ ...

  7. 分布式缓存系统Redis原理解析

    Redis作为内存数据库已经广泛应用于大数据领域,已经成为分布式架构下的基础组件.本文主要介绍了Redis内部的实现原理包括IO模型.内存管理.数据持久化等以及三种集群架构,旨在了解其中的实现机制. ...

  8. 分布式一致性协议 Gossip 和 Redis 集群原理解析

    分布式一致性协议 Gossip 和 Redis 集群原理解析 Redis 是一个开源的.高性能的 Key-Value 数据库.基于 Redis 的分布式缓存已经有很多成功的商业应用,其中就包括阿里 A ...

  9. clickhouse原理解析与应用实践_编程好书推荐《Redis 深度历险:核心原理与应用实践》...

    今天看到一本书,叫<Redis 深度历险:核心原理与应用实践>,作者叫钱文品(老钱),目前在掌阅科技出任资深开发工程师,这本书对redis的剖析还是挺深入的 对redis感兴趣的朋友可以买 ...

最新文章

  1. gprof 性能优化工具
  2. C#只能lock 引用类型的值 (转载)
  3. Windows 技巧篇-点开头的文件夹名创建方法。如何创建点开头的文件夹?
  4. Perl中use、require的用法和区别
  5. JAVA的IO编程:管道流
  6. linux gradle解压后, 执行gradle error13: Permission denied
  7. dotNET Core 3.X 使用 Autofac 来增强依赖注入
  8. [hackinglab][CTF][解密关][2020] hackinglab 解密关 writeup
  9. ApacheCN React 译文集 20211118 更新
  10. mysql 临时列_在MySQL中添加一个带有值的临时列?
  11. 图像算法十:轮廓匹配match_contours() 得到精确的旋转角度
  12. Haclon 一些关于显示的基本算子(1)
  13. Python迭代器的生成与学习笔记
  14. 【Java】JDBC基础使用教程
  15. 橡胶软接头加防拉脱限位装置
  16. TOP Network技术总监Justin:TOP公链已率先实现多层状态分片
  17. hotspot解释器和JIT
  18. 室友在宿舍玩游戏我学java_在宿舍写代码总被一个室友认为在装逼,该怎么办?...
  19. TCP聊天文件服务器v2.0 - 重大bug修复+PyQt5文件传输可视化
  20. 程序员副业之如何利用空余时间从博客中赚钱?

热门文章

  1. strcpy系列函数
  2. 1379 八数码难题
  3. 融合统计机器翻译特征的蒙汉神经网络机器翻译技术
  4. 【SICP练习】146 练习4.2
  5. linux tf 卡驱动理解
  6. java 方法 throws_Java异常处理之------Java方法中throws Exception使用案例!什么情况下使用throws Exception?...
  7. 绘制数据流程图操作方法
  8. 主机甲和主机乙间已建立一个TCP连接,主机甲向主机乙发送了两个连续的TCP段,分别包含300字节和500字节的有效载荷,第一个段的序列号为200,主机乙正确接收到两个段后,发送给主机甲的确认序列号是?
  9. JavaFx:快捷键
  10. Java——多线程之方法详解