实现分布式锁

实现分布式锁的两个核心:
一、获取锁
1、获取锁线程互斥性
为了实现只有一个线程能继续执行业务代码,必须保证获取锁具有互斥性,即只有一个线程能获取到锁。
Redis中操作数据是单线程的,可以使用Redis提供的set nx ex命令获取锁。
spring底层封装set nx ex命令实现获取锁的互斥性。stringRedisTemplate.opsForValue().setIfAbsent(K key, V value, long timeout, TimeUnit unit)
二、释放锁
1、释放锁误删问题
必须释放当前线程加的锁。
场景:线程1阻塞,导致锁超时自动释放,线程2获取锁执行业务,这时线程1被唤醒执行释放锁操作,但线程1实际释放线程2的锁,导致线程3能获取到锁进而造成线程2和线程3的并发问题。
解决:获取锁时存入线程标识(uuid-threadid),基于线程标识释放锁。线程标识不能是线程id,因为不同服务的线程id可能相同。
2、释放锁的原子性问题
场景:线程1释放锁时先判断线程标识,还未释放锁,此时线程阻塞(网络或GC,GC时线程会阻塞),锁自动过期,线程1被唤醒后删除了其他线程的锁。
解决:lua脚本保证分布式锁原子性。判断和删除为同一原子操作,成功返回true失败返回false。
if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end

#参考文档
https://blog.csdn.net/qq_36602071/article/details/126002886

写死脚本方式 

package hh.club.hisape.server.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @GetMapping("/testRedisson")
    public void testRedisson() {
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        Boolean bool = redisTemplate.opsForValue().setIfAbsent("lock:test", uuid + "-" + Thread.currentThread().getId(), 10, TimeUnit.SECONDS);
        if (bool) {
            System.out.println("获取锁成功");
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Long execute = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList("lock:test"), uuid + "-" + Thread.currentThread().getId());
            if (1L == execute) {
                System.out.println("释放锁成功");
            } else {
                System.out.println("释放锁失败");
            }
        } else {
            System.out.println("获取锁失败");
        }
    }
}

读取配置文件lua脚本方式

package hh.club.hisape.server.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @GetMapping("/testRedisson")
    public void testRedisson() {
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        Boolean bool = redisTemplate.opsForValue().setIfAbsent("lock:test", uuid + "-" + Thread.currentThread().getId(), 10, TimeUnit.SECONDS);
        if (bool) {
            System.out.println("获取锁成功");
            DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript();
            defaultRedisScript.setLocation(new ClassPathResource("unlock.lua"));
            defaultRedisScript.setResultType(Long.class);
            Long execute = redisTemplate.execute(defaultRedisScript, Collections.singletonList("lock:test"), uuid + "-" + Thread.currentThread().getId());
            if (1L == execute) {
                System.out.println("释放锁成功");
            } else {
                System.out.println("释放锁失败");
            }
        } else {
            System.out.println("获取锁失败");
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙原生应用元服务-访问控制(权限)开发等级和类型

一、权限等级说明 根据接口所涉数据的敏感程度或所涉能力的安全威胁影响&#xff0c;ATM模块定义了不同开放范围的权限等级来保护用户隐私。 应用APL等级说明 元能力权限等级APL&#xff08;Ability Privilege Level&#xff09;指的是应用的权限申请优先级的定义&#xff0c;…

Ubuntu 22.04 安装 zabbix

Ubuntu 22.04 安装 zabbix 1&#xff0c;Install Zabbix repository2&#xff0c;安装Zabbix server&#xff0c;Web前端&#xff0c;agent3&#xff0c;安装mysql数据库3.1 创建初始数据库3.2 导入初始架构和数据&#xff0c;系统将提示您输入新创建的密码。3.3 在导入数据库架…

课题学习(二十一)----姿态更新的四元数算法推导

声明&#xff1a;本人水平有限&#xff0c;博客可能存在部分错误的地方&#xff0c;请广大读者谅解并向本人反馈错误。    最近需要使用AEKF对姿态进行结算&#xff0c;所以又对四元数进了深入的学习&#xff0c;本篇博客仅对四元数进行推导&#xff0c;后续会对基于四元数的…

kafka学习笔记03

SpringBoot2.X项目搭建整合Kafka客户端依赖配置 用自己对应的jdk版本。 先加上我们的web依赖。 添加kafka依赖: SpringBoot2.x整合Kafka客户端adminApi单元测试 设置端口号。 新建一个kafka测试类&#xff1a; 创建一个初始化的Kafka服务。 设置kafka的名称。 测试创建kafka。…

Gitee和Git学习笔记

Gitee和Git指令 Gitee提交代码方法1 先将仓库clone到本地&#xff0c;修改后再push到 Gitee 的仓库方法2 本地初始化一个仓库&#xff0c;设置远程仓库地址后再做push 切换分支下载代码通过git clone克隆仓库通过下载 ZIP 的方式下载代码 Git提交指令 解决本地库同时关联GitHub…

数据库SQL语言实战(三)

删除操作 本篇文章重点在于SQL中的各种删除操作 题目一 删除表中的学号不全是数字的那些错误数据&#xff0c;学号应该是数字组成&#xff0c;不能够包含字母空格等非数字字符。方法之一&#xff1a;用substr函数&#xff0c;例如Substr(sid,1,1)返回学号的第一位&#xff0…

数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信您对博主首页也很感兴趣o (ˉ▽ˉ&#xff1b;) 博主首页&#xff0c;更多redis、java等优质好文以及各种保姆级教程等您挖掘&#xff01; 目录 需求分析 常用案例举例 加盐加密逻辑如何对比原数据&…

分布式光纤测温解决方案

安科瑞电气股份有限公司 祁洁 15000363176 一、方案介绍 分布式光纤测温&#xff08;DTS&#xff09;集光电信号检测、计算机技术等为一体&#xff0c;具有实时监测、测温精度高、测量距离长、可精确定位、采用光纤作为传感器和传输介质&#xff0c;具有抗电磁干扰、本征防…

GVRP协议与动态、静态vlan

一、GVRP协议使用场景 1、当实际组网复杂到网络管理员无法短时间内了解网络的拓扑结构&#xff0c;或者是整个网络的VLAN太多时&#xff0c;工作量会非常大&#xff0c;而且非常容易配置错误。在这种情况下&#xff0c;用户可以通过GVRP的VLAN自动注册功能完成VLAN的配置。 2、…

【Vue3】setup语法糖的使用

文章目录 setup简介使用vite-plugin-vue-setup-extend插件 指定组件名字 setup简介 <script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖 相比较普通的<script> ,它有以下优势&#xff1a; 更少的样板内容&#xff0c;更简洁的代码。能够使用纯…

【教程】如何使用ArcPy快速批量的处理数据

前面介绍了如何构建自己的ArcGIS工具箱&#xff0c;能够极大地减轻繁琐重复的工作&#xff0c;可查看&#xff1a; 【教程】如何自制一个ArcGIS工具箱&#xff08;ArcPy和模型构建器的使用&#xff09; 除了制作工具箱来实现自动处理重复性的工作&#xff0c;还可以使用ArcPy…

解决Error (169281)、Error (169282)报错问题,QuartusII设置Virtual Pin虚拟管脚的详细操作方法

解决Error(169281)、Error(169282)报错问题,QuartusII设置Virtual Pin虚拟管脚的详细操作方法 1,QuartusII报错信息2,解决办法3,重新编译,成功参考文献: 1,Quartus如何设置虚拟管脚Virtual Pin(具体设置方法) 1,QuartusII报错信息 报错原因:    为了验证FPGA工…

vr兽医设备操作模拟仿真教学平台提升教学效果

在兽医教育的传统领域中&#xff0c;动物诊疗一直是一项不可或缺的实践环节。然而&#xff0c;传统的解剖教学方式受限于动物数量、种类以及安全隐患&#xff0c;无法充分满足学生的学习需求。随着VR虚拟仿真技术的不断精进&#xff0c;VR动物诊疗仿真实训系统为兽医教育带来了…

福州复式装修,115平四室三厅现代简约风。福州中宅装饰,福州装修

设计亮点 设计理念&#xff1a; 静享时光谧境 克制的优雅&#xff0c;简约的沉淀 以光为引&#xff0c;以意为境 案例简介&#xff1a; 该方案现代风格为整个设计带来现代的舒适感&#xff0c;各种材质相互碰撞的设计&#xff0c;即保持着整齐的视感&#xff0c;又将高级气质凸…

所有人记住!电商选品千万别学会这6个步骤!我怕你流量池爆掉!

电商选品是一个非常重要的环节&#xff0c;它直接关系到店铺的流量、销售以及客户的满意度&#xff0c;做好选品才能打造爆款产品&#xff0c;提升流量和销售。因此&#xff0c;正确的电商选品步骤是至关重要的。以下是一些店雷达帮大家梳理的关键电商选品步骤以及运营建议&…

全国产化无风扇嵌入式车载电脑在救护车远端诊断的行业应用

救护车远端诊断的行业应用 背景介绍 更加快速的为急症病人在第一时间开始进行诊断和治疗,是提高病人救助成功率的关键。因此&#xff0c;先进的救护系统正在思考&#xff0c;如何在病人进入救护车之后&#xff0c;立刻能够将救护车中各种检查仪器的信息快速的传回医院&#xf…

移动端vue3使用pdfjs在浏览器上面运行正常,在移动端页面报错出现空白页

1.PDFjs文件包&#xff1a; 分享一下PDFjs文件包&#xff0c;这是我在其他博客那里找到的&#xff0c;找了好久&#xff0c;在官网下载一天了&#xff0c;一直下载失败&#xff0c;只能去找其他人的。我也想把这个包分享给大家&#xff0c;真心好用。一开始我的浏览器页面一直…

黄仁勋最新访谈:GPU性能的革命性提升与AI未来

近期&#xff0c;英伟达CEO黄仁勋与美国CNBC知名主持人、股评人吉姆克莱默&#xff08;Jim Cramer&#xff09;在《Mad Money》节目中展开了一场关于技术未来和人工智能的对话。访谈里&#xff0c;黄仁勋不仅提到了英伟达在过去八年中将AI算力性能提高1000倍&#xff0c;还预言…

前端Vue3+uni+Ts

本次记录小兔仙仙的制作过程。 先看下我们的项目截图。主要是手机端&#xff0c;这里用了uniappVScode.三端适配的。可以打包成安卓和苹果。微信小程序。 首先&#xff1a;创建一个uni新的ts项目。 # 通过 git 从 gitee 克隆下载 登录 - Gitee.com git clone -b vite-ts http…

记录-海思开发板的 嵌入式nginx和 php的移植(交叉编译环境配置)

嵌入式 lnmp搭建的记录 N&#xff1a;NginxP&#xff1a;php编译PHP可能遇到的问题configure阶段&#xff1a;Makefile-make阶段&#xff1a;Makefile-make install阶段&#xff1a; 文章比较水&#xff0c;并没有没解决什么实际问题&#xff0c;有点不好意思发布。但好像又记录…