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

工具类

@Service
public class RedissonManager {@Autowiredprivate RedissonClient redissonClient;/*** 加锁** @param lockKey* @return*/public RLock lock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);lock.lock();return lock;}/*** 释放锁** @param lockKey*/public void unlock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);lock.unlock();}/*** 释放锁** @param lock*/public void unlock(RLock lock) {lock.unlock();}/*** 带超时的锁** @param lockKey* @param timeout 超时时间   单位:秒*/public RLock lock(String lockKey, int timeout) {RLock lock = redissonClient.getLock(lockKey);lock.lock(timeout, TimeUnit.SECONDS);return lock;}/*** 带超时的锁** @param lockKey* @param unit    时间单位* @param timeout 超时时间*/public RLock lock(String lockKey, TimeUnit unit, int timeout) {RLock lock = redissonClient.getLock(lockKey);lock.lock(timeout, unit);return lock;}/*** 尝试获取锁** @param lockKey* @param waitTime  最多等待时间* @param leaseTime 上锁后自动释放锁时间* @return*/public boolean tryLock(String lockKey, int waitTime, int leaseTime) {RLock lock = redissonClient.getLock(lockKey);try {return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);} catch (InterruptedException e) {return false;}}/*** 尝试获取锁** @param lockKey* @param unit      时间单位* @param waitTime  最多等待时间* @param leaseTime 上锁后自动释放锁时间* @return*/public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {RLock lock = redissonClient.getLock(lockKey);try {return lock.tryLock(waitTime, leaseTime, unit);} catch (InterruptedException e) {return false;}}}

实际使用很简单,就是直接使用方法来锁住一个key,但是后续测试发现lock和tryLock是两种不同的情况。
lock是当获取锁失败时会阻塞当前进程,如果没有带参数设置过期时间则是30秒后自动解锁。
tryLock则是当获取锁失败时,当超过设置的等待时间时返回false

后面楼主出于好奇便看了一下redisson源码以及结合网上大神的见解,略为理解了一下,以此记录一下个人见解(不对请大家积极指出

lock

 private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {// 此处为获取当前线程idlong threadId = Thread.currentThread().getId();// 核心代码见下图后继续回来走逻辑Long ttl = tryAcquire(-1, leaseTime, unit, threadId);// 此处得到获取锁的结果,正常获取锁则ttl为null,竞争锁时返回锁的过期时间        if (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));}

tryLockInnerAsync

    <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都没有进入,则代表处于竞争锁的情况,后续返回竞争锁的过期时间。

tryLock

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

  @Overridepublic 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;}// 以上与lock逻辑一致// 获取锁失败后,中途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,且携带参数,因为可设置最大等待时间以及可及时获取加锁返回值,后续可做一些其他加锁失败的业务

以上是个人理解,如有不对,希望各位大神指出修改

redisson lock、tryLock分布式锁原理解析相关推荐

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

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

  2. Redis分布式锁原理解析

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

  3. Redisson 实现分布式锁原理

    Redisson实现分布式锁 有关Redisson作为实现分布式锁,总的分3大模块来讲. 1.Redisson实现分布式锁原理 2.Redisson实现分布式锁的源码解析 3.Redisson实现分布 ...

  4. Redisson实现分布式锁原理

    Redisson实现分布式锁原理 一.高效分布式锁 当我们在设计分布式锁的时候,我们应该考虑分布式锁至少要满足的一些条件,同时考虑如何高效的设计分布式锁,这里我认为以下几点是必须要考虑的. 1.互斥 ...

  5. 一文掌握Redisson分布式锁原理|干货推荐

    ReentrantLock 重入锁 在说 Redisson 之前我们先来说一下 JDK 可重入锁: ReentrantLock ReentrantLock 保证了 JVM 共享资源同一时刻只允许单个线 ...

  6. Redisson实现Redis分布式锁的N种姿势

    点击蓝色"程序猿DD"关注我哟 来源:阿飞的博客 前几天发的一篇文章<Redlock:Redis分布式锁最牛逼的实现>,引起了一些同学的讨论,也有一些同学提出了一些疑问 ...

  7. Redission 分布式锁原理

    Reddission 分布式锁原理 总结: 使用无参的tryLock()方法时,redisson会自动添加一个定时任务,定时刷新锁的失效时间,如果unlock时失败,则会出现该锁一直不释放的情况, 因 ...

  8. redis和redission分布式锁原理及区别

    redis和redission分布式锁原理及区别 我最近做租车项目,在处理分布式时用到分布式锁,我发现很多同事都在网上找分布式锁的资料,但是看的资料都不是很全,所以在这里我谈谈自己的分布式锁理解. 结 ...

  9. redis 分布式锁 看门狗_redis分布式锁原理及实现

    一.写在前面 现在面试,一般都会聊聊分布式系统这块的东西.通常面试官都会从服务框架(Spring Cloud.Dubbo)聊起,一路聊到分布式事务.分布式锁.ZooKeeper等知识. 所以咱们这篇文 ...

最新文章

  1. 我说分布式事务之最大努力通知型事务
  2. 谷歌SEO和百度SEO的区别
  3. java调用 火眼臻睛,连接创新,发现未来,臻识科技受邀CCF-GAIR,论道浪潮之巅...
  4. 使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历
  5. mysql 笔记打包下载_mysql 5.7压缩包安装笔记
  6. ubuntu下中文输入法的安装--fcitx
  7. .net网页input 赋值 提交时空白_《快速掌握PyQt5》第三十章 网页交互QWebEngineView...
  8. [洛谷P3242] [HNOI2015]接水果
  9. str split函数 php,怎么在php中利用str_split函数分割字符串
  10. css的z-index属性,div折腾了好久
  11. win8.1使用及优化
  12. dependencies.dependency.version' for org.hibernate:hibernate-validator:jar is missing.
  13. SpringMVC-01-基本组件与注解式编程
  14. paip.网站上传服务里需要做的操作流程V2012.10.2
  15. 虚拟IP技术-VIP 与 负载均衡
  16. C/C++ _beginthreadex 多线程操作 - 线程同步
  17. 图表控件ScottPlot
  18. SolidWorks迈迪轴生成器使用注意事项
  19. ArcGIS超级工具目录
  20. 两个人体红外传感器计数,判断屋里的人数的单片机程序

热门文章

  1. JRebel入门使用
  2. 微信小程序统一分享,全局接管页面分享消息的一些技巧
  3. SpringBootAdmin小记
  4. CADD计算机辅助药物设计+AIDD人工智能药物发现与设计专题
  5. 如何成为优秀的机器学习工程师?
  6. 云呐|企事业单位如何选择固定资产盘点管理系统
  7. 【Android】 Color 颜色设置
  8. 日常Java练习题(每天进步一点点系列)【含资料】
  9. 这个“忽悠”马云10亿的男人,当选院士!
  10. html静态网页:自动出题评分系统