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

news2025/1/20 6:03:42

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

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

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

lock() 与 tryLock() 的区别

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

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

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

tryLock()

@Override
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
    // 转成毫秒,后面都是以毫秒为单位
    long time = unit.toMillis(waitTime);
    // 当前时间
    long current = System.currentTimeMillis();
    // 线程ID-线程标识
    long threadId = Thread.currentThread().getId();
 
    // 尝试获取锁 tryAcquire() !!!
    Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
        
    // 如果上面尝试获取锁返回的是null,表示成功;如果返回的是时间则表示失败。
    if (ttl == null) {
        return true;
    }
 
    // 剩余等待时间 = 最大等待时间 -(用现在时间 - 获取锁前的时间)
    time -= System.currentTimeMillis() - current;
 
    // 剩余等待时间 < 0 失败
    if (time <= 0) {
        acquireFailed(waitTime, unit, threadId);
        return false;
    }
 
    // 再次获取当前时间
    current = System.currentTimeMillis();
    // 重试逻辑,但不是简单的直接重试!
    // subscribe是订阅的意思
    RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
    // 如果在剩余等待时间内,收到了释放锁那边发过来的publish,则才会再次尝试获取锁
    if (!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;
        }
 
        // 重试!
        while (true) {
            long currentTime = System.currentTimeMillis();
            ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
 
            // 成功
            if (ttl == null) {
                return true;
            }
            
            // 又获取锁失败,再次计算上面的耗时
            time -= System.currentTimeMillis() - currentTime;
            if (time <= 0) {
                acquireFailed(waitTime, unit, threadId);
                return false;
            }
 
            currentTime = 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);
    }
}

lock()

private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {

	// 获取当前线程 ID
    long threadId = Thread.currentThread().getId();
    // 获取锁,正常获取锁则ttl为null,竞争锁时返回锁的过期时间
    Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
    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);
            if (ttl == null) {
                break;
            }

            if (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);
    }
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/69299.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue中的diff算法深度解析

模板tamplate经过parse&#xff0c;optimize&#xff0c;generate等一些列操作之后&#xff0c;把AST转为render function code进而生成虚拟VNode,模板编译阶段基本已经完成了&#xff0c;那么这一章&#xff0c;我们来探讨一下Vue中的一个算法策略–dom diff 首先来介绍下什么…

Java8 遍历List 使用stream().parallel()并发安全

1. parallelStream是什么&#xff1a; java 8引入了并行流的概念来进行并行处理&#xff0c;而并行流(Parallel Stream)利用所有可用CPU内核的优势&#xff0c;并行处理任务。其原理(Parallel Stream)是可以把大任务分成多个小任务执行, 最后再把执行结果进行合并, ForkJoinPoo…

数仓DWS层之旁路缓存优化

优化原因&#xff1a; 外部数据源的查询常常是流式计算的性能瓶颈。以本程序为例&#xff0c;每次查询都要连接 Hbase&#xff0c;数据传输需要做序列化、反序列化&#xff0c;还有网络传输&#xff0c;严重影响时效性。可以通过旁路缓存对查询进行优化。 旁路缓存模式是一种非…

利用Python海龟绘图画一个世界杯的足球

利用Python海龟绘图画一个世界杯的足球 花有重开日 人无再少年 四年一次的世界杯快要结束&#xff0c;为了纪念此次世界杯&#xff0c;特意用Python画了一个足球。 1.设计思路以及实现效果 世界杯足球实现思路&#xff1a; 首先使用海龟画一个圆形作为足球的外边框。然后在足…

3天带你走向实战!阿里顶配版Spring全家桶面试进阶笔记有多强?

Spring框架自从诞生以来就一直备受开发者青睐&#xff0c;它涵盖了Spring、Springboot、SpringCloud等诸多解决方案&#xff0c;一般我们都会统称为Spring全家桶&#xff01;出于Spring框架在Java开发者心中中的统治地位&#xff0c;所以不管是面试还是工作&#xff0c;Spring都…

夜神模拟器+fiddler抓包(抓取APPhttps请求,删除sll证书校验)

1.安装fiddler https://telerik-fiddler.s3.amazonaws.com/fiddler/FiddlerSetup.exe &#xff08;下载不了直接去官网找&#xff09; 2.配置 开启https请求抓取&#xff0c;不抓https可忽略2.修改或查看端口&#xff08;使用默认8888端口&#xff0c;要自定义端口可修改&#…

Arduino 定时器中断

Arduino 定时器中断 Circuits Arduino 查看原文 简介&#xff1a;Arduino 定时器中断 奥雷里&#xff08;地球、月亮和太阳&#xff09; 立式兰花播种机 胶合板书柜扬声器 计时器中断允许您以非常特定的时间间隔执行任务&#xff0c;而不管代码中发生了什么其他事情。我…

Unity ILRuntime Debugger使用及常见问题

目录前言1.安装2.使用3.常见问题前言 ILRuntime支持在VS中断点调试&#xff0c;下面说一下ILRuntime Debugger的使用及常见问题。 1.安装 需要下载对应版本的ILRuntime Debugger VS插件。我是在Unity中PackageManager安装的ILRuntime&#xff0c;可以在插件信息中查看版本。…

记SQL插入emoji成功,但是程序插入失败问题

在执行单测时&#xff0c;碰到了以下熟悉的问题 org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: java.sql.SQLException: Incorrect string value: \xF0\x9F\x92\x8B for column name at row 1 ### The error may involve com.*…

Java入门教程(16)——条件判断语句

文章目录1. if结构1.1 if 单分支结构1.2 if-else 双分支结构1.3 if-else if-else 多分支结构switch 语句switch 多分支结构1. if结构 1.1 if 单分支结构 语法结构: if(布尔表达式){ 语句块 }实例&#xff1a;掷色子游戏 这里给大家扩展一个Math函数 Math.Random()&#xff0c…

动态规划算法

1.简介 1.动态规划(Dynamic Programming)算法的核心思想是: 将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法; 2.动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解; 3.与分治法不同…

项目统一规范包管理器

一般来说每个团队都会统一规定项目内只使用一个包管理器&#xff0c;譬如&#xff1a;npm、yarn、pnpm等&#xff0c;我们可以在文档中或者项目根目录REDEM.md中进行描述来形成共识&#xff0c;但毕竟是文档&#xff0c;并不能真正的进行约束&#xff0c;如果有项目成员没有看文…

SpringBoot自动装配原理分析,看完你也能手写一个starter组件

什么是 SpringBoot 2012 年 10 月&#xff0c;一个叫 Mike Youngstrom 的人在 Spring Jira 中创建了一个功能请求&#xff0c;要求在 Spring Framework 中支持无容器 Web 应用程序体系结构&#xff0c;提出了在主容器引导 Spring 容器内配置 Web 容器服务。这件事情对 SpringBo…

Linux 进程间通信

目录 进程间通信的必要性 进程间通信的技术背景 进程间通信的本质理解&#xff1a; 管道IPC&#xff1a;匿名管道 示意图 匿名管道的本质原理&#xff1a; demo示例代码&#xff1a; pipe 系统调用 注意&#xff1a; 管道读写的4种情况&#xff1a; 管道的特点&…

H5UI库和二维码

一、H5UI库 1、使用方法&#xff1a; ​ &#xff08;1&#xff09;页面中引入css文件 ​ h5ui.css &#xff08;h5ui.min.css&#xff09; ​ &#xff08;2&#xff09;页面中引入js文件 ​ jquery.min.js ​ h5ui.min.js 2、组件的用法 ​ &#xff08;1&#xff09…

为您的高速SPI添加强大和可靠的隔离交流

介绍 串行外设接口&#xff08;SPI&#xff09;是工业设备中常用于数字处理器核心和外围设备之间通信的一种协议。然而&#xff0c;为了安全使用&#xff0c;有必要对外围设备和核心进行电隔离。虽然隔离和SPI都是成熟的技术&#xff0c;但将两者接口并不像预期的那么简单。 …

SAP ABAP——数据类型(五)【LIKE系列关键字】

&#x1f4ac;个人网站&#xff1a;【芒果个人日志】​​​​​​ &#x1f4ac;原文地址&#xff1a;SAP ABAP——数据类型&#xff08;五&#xff09;【LIKE系列关键字】 - 芒果个人日志 (wyz-math.cn) &#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税…

【git】简洁实用教程

虽然之前有git的笔记了&#xff0c;但是操作和命令太多&#xff0c;有点冗余&#xff0c;下面整理出最常见的一些场景和git需求。 零、Git速查表 好习惯&#xff1a;每次提交后和开发代码前&#xff0c;都应该pull下 常见命令&#xff1a; git clone拉取服务器代码&#xff0…

深度解读 | 如何构建以指标为核心的ABI平台?

在上期一文中&#xff0c;我们了解到BI不同发展阶段运行模式及遇到的问题。“报表阶段”是以报表粒度进行管理&#xff0c;数据和报表完全耦合在一起&#xff0c;在不同报表间产生数据和指标的冗余和重复&#xff0c;形成报表爆炸、技术债&#xff0c;导致数据不可信、分析不敏…

Windows 7下安装oracle12c报错:O/S-Error:(OS 1385)

查看报错日志&#xff1a;C:\Program Files\Oracle\Inventory\logs\ installActions2015-04-21_09-29-15AM.log, 提示查看&#xff1a; D:\app\Administrator\cfgtoollogs\netca\trace_OraDB12Home1-150421 11上午1616.log &#xff0c; 打开该log&#xff0c;在尾部发现如下错…