• Redisson之lock()和tryLock()的区别


    Redisson之lock()和tryLock()的区别和原理解析

    在Redisson中 lock() 方法 与 tryLock() 方法是有区别的!

    我们先来阐述两者的区别,再分析它们的源码。

    lock() 与 tryLock() 的区别

    (1)返回值: lock() 是没有返回值的;tryLock() 的返回值是 boolean。

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

    (3)tryLock() 是可以被打断的,被中断的;lock是不可以。

    tryLock()

    1. @Override
    2. public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
    3. // 转成毫秒,后面都是以毫秒为单位
    4. long time = unit.toMillis(waitTime);
    5. // 当前时间
    6. long current = System.currentTimeMillis();
    7. // 线程ID-线程标识
    8. long threadId = Thread.currentThread().getId();
    9. // 尝试获取锁 tryAcquire() !!!
    10. Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
    11. // 如果上面尝试获取锁返回的是null,表示成功;如果返回的是时间则表示失败。
    12. if (ttl == null) {
    13. return true;
    14. }
    15. // 剩余等待时间 = 最大等待时间 -(用现在时间 - 获取锁前的时间)
    16. time -= System.currentTimeMillis() - current;
    17. // 剩余等待时间 < 0 失败
    18. if (time <= 0) {
    19. acquireFailed(waitTime, unit, threadId);
    20. return false;
    21. }
    22. // 再次获取当前时间
    23. current = System.currentTimeMillis();
    24. // 重试逻辑,但不是简单的直接重试!
    25. // subscribe是订阅的意思
    26. RFuture subscribeFuture = subscribe(threadId);
    27. // 如果在剩余等待时间内,收到了释放锁那边发过来的publish,则才会再次尝试获取锁
    28. if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
    29. if (!subscribeFuture.cancel(false)) {
    30. subscribeFuture.onComplete((res, e) -> {
    31. if (e == null) {
    32. // 取消订阅
    33. unsubscribe(subscribeFuture, threadId);
    34. }
    35. });
    36. }
    37. // 获取锁失败
    38. acquireFailed(waitTime, unit, threadId);
    39. return false;
    40. }
    41. try {
    42. // 又重新计算了一下,上述的等待时间
    43. time -= System.currentTimeMillis() - current;
    44. if (time <= 0) {
    45. acquireFailed(waitTime, unit, threadId);
    46. return false;
    47. }
    48. // 重试!
    49. while (true) {
    50. long currentTime = System.currentTimeMillis();
    51. ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
    52. // 成功
    53. if (ttl == null) {
    54. return true;
    55. }
    56. // 又获取锁失败,再次计算上面的耗时
    57. time -= System.currentTimeMillis() - currentTime;
    58. if (time <= 0) {
    59. acquireFailed(waitTime, unit, threadId);
    60. return false;
    61. }
    62. currentTime = System.currentTimeMillis();
    63. // 采用信号量的方式重试!
    64. if (ttl >= 0 && ttl < time) {
    65. subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
    66. } else {
    67. subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
    68. }
    69. // 重新计算时间(充足就继续循环)
    70. time -= System.currentTimeMillis() - currentTime;
    71. if (time <= 0) {
    72. acquireFailed(waitTime, unit, threadId);
    73. return false;
    74. }
    75. }
    76. } finally {
    77. unsubscribe(subscribeFuture, threadId);
    78. }
    79. }

    lock()

    1. private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
    2. // 获取当前线程 ID
    3. long threadId = Thread.currentThread().getId();
    4. // 获取锁,正常获取锁则ttl为null,竞争锁时返回锁的过期时间
    5. Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
    6. if (ttl == null) {
    7. return;
    8. }
    9. // 订阅锁释放事件
    10. // 如果当前线程通过 Redis 的 channel 订阅锁的释放事件获取得知已经被释放,则会发消息通知待等待的线程进行竞争
    11. RFuture future = subscribe(threadId);
    12. if (interruptibly) {
    13. commandExecutor.syncSubscriptionInterrupted(future);
    14. } else {
    15. commandExecutor.syncSubscription(future);
    16. }
    17. try {
    18. while (true) {
    19. // 循环重试获取锁,直至重新获取锁成功才跳出循环
    20. // 此种做法阻塞进程,一直处于等待锁手动释放或者超时才继续线程
    21. ttl = tryAcquire(-1, leaseTime, unit, threadId);
    22. if (ttl == null) {
    23. break;
    24. }
    25. if (ttl >= 0) {
    26. try {
    27. future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
    28. } catch (InterruptedException e) {
    29. if (interruptibly) {
    30. throw e;
    31. }
    32. future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
    33. }
    34. } else {
    35. if (interruptibly) {
    36. future.getNow().getLatch().acquire();
    37. } else {
    38. future.getNow().getLatch().acquireUninterruptibly();
    39. }
    40. }
    41. }
    42. } finally {
    43. // 最后释放订阅事件
    44. unsubscribe(future, threadId);
    45. }
    46. }

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

  • 相关阅读:
    ShardingSphere 集成 CosId 实战
    AutoGluon学习笔记
    安全渗透初级知识总结-2
    迟滞比较器仿真
    jsp-一篇就够
    禁止ios和android用户选中文字
    【无标题】
    SharePreference与MMKV对比
    Linux操作系统——系统用户与用户组管理
    暑假加餐|有钱人和你想的不一样(第6天)+改进教学的优化算法(TLSBO)(Matlab代码实现)
  • 原文地址:https://blog.csdn.net/weixin_43715214/article/details/128212336