Redisson-Lock-加锁原理

归档

  • GitHub: Redisson-Lock-加锁原理

Unit-Test

  • RedissonLockTest

说明

  • 源码类:RedissonLock
// 加锁入口
@Override
public void lock() { lock(-1, null, false);
}/*** 加锁实现 */
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) {long threadId = Thread.currentThread().getId();Long ttl = tryAcquire(-1, leaseTime, unit, threadId);if (ttl == null) {return; // 加锁成功,返回}// 加锁失败进行订阅CompletableFuture<RedissonLockEntry> future = subscribe(threadId); pubSub.timeout(future);RedissonLockEntry entry;if (interruptibly) {entry = commandExecutor.getInterrupted(future);} else { // 默认进入这一步entry = commandExecutor.get(future);}try {while (true) { // 循环尝试加锁ttl = tryAcquire(-1, leaseTime, unit, threadId);// lock acquiredif (ttl == null) { // 获锁成功break;}...}} finally {// 加锁成功退出时,取消订阅unsubscribe(entry, threadId);}
}/*** 尝试获取锁 */
private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {// 调用异步获取锁,get() 转换成同步return get(tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}/*** 异步获取锁 */
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {RFuture<Long> ttlRemainingFuture;if (leaseTime > 0) {ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);} else { // 默认进入这一步ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);}CompletionStage<Long> f = ttlRemainingFuture.thenApply(ttlRemaining -> {// 获锁成功的回调// lock acquiredif (ttlRemaining == null) {if (leaseTime > 0) {internalLockLeaseTime = unit.toMillis(leaseTime);} else { // 默认进入这一步// 开启锁续期定时任务scheduleExpirationRenewal(threadId);}}return ttlRemaining;});return new CompletableFutureWrapper<>(f);
}/*** Lua 获锁实现 */
<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) " + // 不存在"or (redis.call('hexists', KEYS[1], ARGV[2]) == 1)) then " + // 或是当前线程"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " + // 设置过期时间,默认 30s"return nil; " + // 返回空,表示获锁成功"end; " +"return redis.call('pttl', KEYS[1]);", // 返回被抢锁的 TTLCollections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
}/*** 锁续约。在父类 RedissonBaseLock 里面 */
protected void scheduleExpirationRenewal(long threadId) {...try {renewExpiration(); // 续约} finally {if (Thread.currentThread().isInterrupted()) {cancelExpirationRenewal(threadId); // 线程中断,取消续约}}
}/*** 锁续约任务,循环调用。在父类 RedissonBaseLock 里面 */
private void renewExpiration() {...Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {...CompletionStage<Boolean> future = renewExpirationAsync(threadId);future.whenComplete((res, e) -> {if (e != null) { // 出现异常,不再续约EXPIRATION_RENEWAL_MAP.remove(getEntryName());return;}if (res) {renewExpiration(); // 调用自己继续续约} else {cancelExpirationRenewal(null); // 锁已不是当前线程的,取消续约}});} // internalLockLeaseTime 默认为 30s(30_000)}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); // 每 10s 续期一次
}/*** Lua 锁续约实现 */
protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('pexpire', KEYS[1], ARGV[1]); " + // 继续设置过期时间,默认 30s"return 1; " + // 是当前线程的"end; " +"return 0;", // 已不是当前线程的了Collections.singletonList(getRawName()),internalLockLeaseTime, getLockName(threadId));
}

流程说明

  • 加锁成功则返回,同时内部开启续约任务(每 10s 一次,续约 30s TTL)
  • 加锁失败,则订阅通道,以获知别的线程释放锁的通知

Ref

  • https://zhuanlan.zhihu.com/p/135864820

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

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

相关文章

低代码平台教你两步把SQL直接转换为RESTful API

文章目录 前言一、简介1. 项目亮点2. 技术栈3. 核心功能4. 数据库设计二、搭建教程1. 目录结构2. 下载2.1 下载2.2 上传2.3 解压3. 配置3.1 创建并切换数据库3.2 执行sql文件3.3 修改配置文件4. 启动/停止4.1 启动4.2 停止4.3 重启5. 浏览器访问三、配置教程1. 数据源配置1.1 创…

Java面试题:讨论synchronized关键字和java.util.concurrent包中的同步工具,如Lock和Semaphore

在 Java 中&#xff0c;synchronized 关键字和 java.util.concurrent 包中的同步工具都是用来控制多线程环境下的并发访问&#xff0c;以防止数据竞争和确保线程安全。下面是对 synchronized 关键字和 java.util.concurrent 包中的一些同步工具的讨论&#xff0c;包括它们的特点…

基于自适应融合和偏置消除的鲁棒松耦合视觉惯性里程计

预印本中文版论文 基于自适应融合和偏置消除的鲁棒松耦合视觉惯性里程计 Frank Fan1 电子邮件: fanzexuan135163.com 摘要 本文提出了一种新颖的松耦合视觉惯性里程计(VIO)算法,该算法解决了MEMS IMU中的显著偏置问题,并在具有挑战性的环境中提供了稳健的性能。我们的方法…

Leetcode Java学习记录——代码随想录哈希表篇

文章目录 哈希表几种哈希实现 Java数组HashSetmap方法charAt()toCharArray()for 遍历长度 哈希表 当需要快速判断一个元素是否出现在集合里的时候&#xff0c;就要用到哈希表。 无限循环就意味着重复出现。 几种哈希实现 数组&#xff1a;大小固定set&#xff1a;只存keymap…

美创科技入选“2024年度浙江省工业信息安全服务支撑单位”

近日&#xff0c;浙江省经济和信息化厅公布“2024年度浙江省工业信息安全服务支撑单位”名单。 经单位自愿申报、各市经信主管部门初审推荐、专家评审等环节&#xff0c;凭借在工业数据安全领域长期深耕和产品服务实力&#xff0c;美创科技成功入选&#xff01; “2024年度浙江…

理解 JTBD 框架和EJ 理念:深挖以用户为中心的设计

在与用户的交流中&#xff0c;我们发现对用户需求的精准洞察普遍困扰着产品经理、设计、企划人员&#xff0c;因为当今消费者行为已经由单品消费转向场景消费&#xff0c;千人千面的个性化需求出现&#xff0c;消费者数据维度极大丰富&#xff0c;这对把握用户体验造成了很大挑…

工具与技术:如何使用工具创建和实现导航栏图标动效

这篇教程的目的主要是带领大家做UI交互的入门引导,让大家理解做交互动效的一些基本逻辑思维,利用原型交互动画做导航栏icon动画效果。 导航栏icon动效的详细教程&#xff1a; 即时设计 - 可实时协作的专业 UI 设计工具即时设计是一款支持在线协作的专业级 UI 设计工具&#x…

2024年道路运输企业主要负责人证模拟考试题库及道路运输企业主要负责人理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年道路运输企业主要负责人证模拟考试题库及道路运输企业主要负责人理论考试试题是由安全生产模拟考试一点通提供&#xff0c;道路运输企业主要负责人证模拟考试题库是根据道路运输企业主要负责人最新版教材&#…

【算法训练记录——Day32】

Day32——贪心算法Ⅱ 1.leetcode122买卖股票的最佳时机II2.leetcode55跳跃游戏3.leetcode45跳跃游戏II4.eetcode1005K次取反后最大化的数组和 目标&#xff1a; leetcode122买卖股票的最佳时机IIleetcode55跳跃游戏leetcode45跳跃游戏IIleetcode1005K次取反后最大化的数组和 1…

掌握数据魔方:Xinstall引领ASA全链路数据归因新纪元

一、引言 在数字化时代&#xff0c;数据是App推广和运营的核心驱动力。然而&#xff0c;如何准确获取、分析并应用这些数据&#xff0c;却成为了许多开发者和营销人员面临的痛点。Xinstall作为一款专业的App全渠道统计服务商&#xff0c;致力于提供精准、高效的数据解决方案&a…

MySQL中的客户端选项(三)

默认情况下&#xff0c;LOAD DATA的LOCAL功能是否可用取决于MySQL客户端库在编译时是否启用了该功能。 当只给出--local-infile&#xff08;不带值&#xff09;时&#xff0c;它启用LOCAL数据加载。当给出--local-infile0时&#xff0c;它禁用LOCAL数据加载。当给出--local-in…

工具分享:Search_Viewer

文章目录 前言Search_Viewer介绍安装方式使用方式 前言 本文推荐工具Search_Viewer&#xff0c;详细介绍其安装和使用方式。 Search_Viewer介绍 集Fofa、Hunter鹰图、Shodan、360 quake、Zoomeye 钟馗之眼、censys 为一体的空间测绘gui图形界面化工具&#xff0c;支持一键采…

CP AUTOSAR标准之LSduRouter(AUTOSAR_CP_SWS_LSduRouter)

1 简介和功能概述 本规范描述了AUTOSAR基础软件模块L-SDU路由器的功能、API和配置。L-SDU路由器模块使用以下模块类型提供路由L-SDU(链路层服务数据单元)的服务: 通信接口模块,使用 < Provider:Up > 或 < Provider:Lo > API,例如EthIf、IEEE1722Tp;L-SDU的路由…

2024数据库期末综合(第9关:索引)

第9关&#xff1a;索引&#xff08;注意看下面的温馨提示&#xff01;&#xff01;&#xff09; 任务描述 湖南人口hnpeople数据表结构如图所示&#xff0c;各字段含义如下 cs&#xff08;城市)、qx(区县)、rk(人口)、man(男)、woman(女)、child(儿童)、adult(成人)、old(老人)…

标题:探索开源世界:2024年热门开源项目推荐

标题&#xff1a;探索开源世界&#xff1a;2024年热门开源项目推荐 在开源文化的推动下&#xff0c;开源项目已经成为技术创新和知识共享的重要平台。它们不仅为程序员提供了学习和交流的机会&#xff0c;也为整个行业带来了前所未有的活力。本文将推荐一些2024年值得关注的热…

PySide在QLabel上按住鼠标左键画方框

用于截图放大等 import sys from PySide6.QtWidgets import QApplication, QLabel, QMainWindow from PySide6.QtGui import QPixmap, QPainter, QPen, QColor from PySide6.QtCore import Qt, QRect, QPointclass ImageLabel(QLabel):def __init__(self, parentNone):super().…

在 Linux 中配置 SSH 连接的加密算法

需要对 OpenSSH 服务器和客户端进行相应配置。以下是具体步骤&#xff1a; 配置 OpenSSH 服务器的加密算法 1. 编辑 OpenSSH 服务器的配置文件&#xff1a; 打开 sshd_config 文件&#xff1a; sudo nano /etc/ssh/sshd_config2. 设置加密算法&#xff08;Ciphers&#xff…

python项目(课设)——飞机大战小游戏项目源码(pygame)

主程序 import pygame from plane_sprites import * class PlaneGame: """ 游戏类 """ def __init__(self): print("游戏初始化") # 初始化字体模块 pygame.font.init() # 创建游戏…

南卡、漫步者和Oladance开放式哪家强?无广避坑测评!

现在市面上的开放式耳机种类非常多&#xff0c;在购买的时候大多数人都没有非常确定的目标&#xff0c;这主要是因为大多数人对开放式耳机的认识程度不够。 作为一个有着多年数码产品测评经验的测评员&#xff0c;我刚好对开放式耳机也有比较深刻的理解&#xff0c;也借着大家…