基于redis的分布式锁

一、redis分布式锁基本信息

1.详细讲解:

Redis 分布式锁是一种用于控制分布式系统中多个进程对共享资源的并发访问的机制。通过 Redis 的原子操作和过期时间功能,可以实现一个简单而有效的分布式锁。接下来,我们将详细介绍其工作原理、基本操作步骤以及代码实现,并提供详细的注释。

2.基本原理

2.1获取锁:

使用 SET 命令尝试设置一个键,并使用 NX 参数(如果键不存在则设置)和 PX 参数(设置键的过期时间)。
如果 SET 操作返回成功(即 OK),则表示获取锁成功。

2.2释放锁:

释放锁时需要确保只有持有锁的客户端才能释放锁,因此需要在释放锁时检查锁的值是否是当前客户端的标识。
使用 Lua 脚本来保证原子性:检查锁的值并删除锁。

二、代码示例

1.redis加锁以及解锁的工具类

1.1注意事项:

1.1.1 由于RedisTemplate交由spring管理,所以我们工具类也需要注册给spring,之后通过注入方式使用工具类,这样redisTemplate就不会是null;
1.1.2 用脚本的方式执行redisTemplate.execute会出现异常java.lang.UnsupportedOperationException: io.lettuce.core.output.ValueOutput does not support set(long),好像是返回值无法转换,由于redis版本不匹配,我这边redis版本是5.0.14.1;(暂未解决脚本执行后续会修改);

@Slf4j
@Component
public class LockUtil {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    //定义变量
    private final Object RELEASE_SUCCESS = 1;
    private final Object RELEASE_ERROR = 0;


    /**
     * 以阻塞方式的获取锁
     * @param key         key
     * @param value       value
     * @param lockTimeout 锁超时时间
     * @param getTimeout  获取锁超时时间
     * @return
     */
    public boolean lockBlock(String key, String value, long lockTimeout, long getTimeout, TimeUnit timeUnit) {
        long start = System.currentTimeMillis();
        //循环执行是否能加锁成功判断
        while (true) {
            //检测是否超时
            if (System.currentTimeMillis() - start > getTimeout) {
                log.error(Thread.currentThread().getName() + "get lock timeout");
                return false;
            }
            //执行set命令 ,如果返回 true,表示获取锁成功;如果返回 false,表示获取锁失败。
            Boolean absent = redisTemplate.opsForValue().setIfAbsent(key, value, lockTimeout, timeUnit);
            //是否成功获取锁
            if (absent != null && absent) {
                return true;
            } else {
                log.info(Thread.currentThread().getName() + "get lock fail:{},{}", key, value);
            }
        }
    }
    
    /**
     * 解锁:由于我这边无法执行redis脚本,一直返回异常,下面redis操作并非是原子性操作
     * @param key 加锁key
     * @param value 锁value
     * @return 返回是否解锁成功
     */
    public boolean unlock(String key, String value) {
        Object result = 0;
        String o = (String) redisTemplate.opsForValue().get(key);
        if (o != null && o.equals(value)) {
            Boolean delete = redisTemplate.delete(key);
            if (delete){
                result = 1;
            }
        }
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        log.error(Thread.currentThread().getName() + "unlock error");
        return false;

    }
}

2.调用实现

采用多线程方式模拟调用

@Slf4j
@RestController
public class TestController {
    @Resource
    private LockUtil lockUtil = new LockUtil();

    @GetMapping(value = "/redisLock")
    public void testRedis(@RequestParam(value = "key") String key) throws ExecutionException, InterruptedException {
        ExecutorService threadPoolTaskExecutor = Executors.newFixedThreadPool(10);
        CompletableFuture<Void> completableFuture1 = this.getCompletableFuture(threadPoolTaskExecutor, key);
        CompletableFuture<Void> completableFuture2 = this.getCompletableFuture(threadPoolTaskExecutor, key);
        CompletableFuture.allOf(completableFuture1, completableFuture2).get();
        threadPoolTaskExecutor.shutdown();
    }

    private CompletableFuture<Void> getCompletableFuture( ExecutorService threadPoolTaskExecutor,String key){
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            String value = UUID.randomUUID().toString();
            try {
                if (lockUtil.lockBlock(key, value, 3L, 10L, TimeUnit.SECONDS)) {
                    log.info(Thread.currentThread().getName() + "获取锁成功,value is {}", value);
                    Thread.sleep(2000);
                } else {
                    log.info(Thread.currentThread().getName() + "获取锁失败,value is {}", value);
                }
            } catch (InterruptedException e) {

                log.info(Thread.currentThread().getName() +"获取锁异常,value is {}", value);
            } finally {
                if (lockUtil.unlock(key, value)) {
                    log.info(Thread.currentThread().getName() + "释放锁,value is {}", value);
                }
            }
        }, threadPoolTaskExecutor);
        return  completableFuture;
    }
}

3.测试结果显示

运行结果

4.上面方法解决的问题

4.1通过对于redis key添加过期防止锁无法释放造成死锁;
4.2通过加锁时间限制防止加锁失败一直加锁,造成死锁;
4.3通过value比对保证解的锁是自己持有的;

5.上面方法存在的问题

5.1代码执行超过redis存放时间:

redis释放之后,别人可以获取锁这样锁就相当于失效,解决方案:看门狗机制,我们redisson已经解决上面问题了

5.2代码实现

1.redisson配置

@Configuration
@Slf4j
public class RedissonConfig {
    @Bean
    public RedissonClient createRedissonClient() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://127.0.0.1:6379");
                //.setPassword(""); // 如果没有密码,可以省略这一行

        return Redisson.create(config);
    }
}

2.代码调用实现

@RestController
public class TestController {

    @Resource
    private RedissonClient redissonClient;

    @GetMapping(value = "/redisLock")
    public void testRedis(@RequestParam(value = "key") String key) throws ExecutionException, InterruptedException {
        ExecutorService threadPoolTaskExecutor = Executors.newFixedThreadPool(10);
        CompletableFuture<Void> completableFuture1 = this.getCompletableFuture(threadPoolTaskExecutor, key);
        CompletableFuture<Void> completableFuture2 = this.getCompletableFuture(threadPoolTaskExecutor, key);
        CompletableFuture.allOf(completableFuture1, completableFuture2).get();
        threadPoolTaskExecutor.shutdown();
    }

    private CompletableFuture<Void> getCompletableFuture(ExecutorService threadPoolTaskExecutor, String key) {
        RLock lock = redissonClient.getLock(key);
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            try {
                // 尝试获取锁,等待时间10秒,上锁时间5秒
                if (lock.tryLock(10, 5, TimeUnit.SECONDS)) {
                    log.info(Thread.currentThread().getName() + "获取锁成功");
                    Thread.sleep(2000);
                } else {
                    log.info(Thread.currentThread().getName() + "获取锁失败");
                }
            } catch (InterruptedException e) {

                log.info(Thread.currentThread().getName() + "获取锁异常");
            } finally {
                lock.unlock();
                log.info(Thread.currentThread().getName() + "释放锁成功");
            }
        }, threadPoolTaskExecutor);
        return completableFuture;
    }
}

3.运行结果

在这里插入图片描述
不足之处,望海涵,希望大佬指点;

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

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

相关文章

Matlab图像处理——细胞图像的分割和计数显示

一. 项目介绍 使用MATLAB编写的细胞图像分割及计数系统&#xff0c;实现了对图像内细胞的计数&#xff0c;以及对每个细胞周长和面积的测量&#xff0c;并分别展示了分割后的每个细胞的图像。实验步骤共分为图像预处理、图像预分割、空洞填充、黏连细胞分割、细胞个数统计、细胞…

LLM 推理框架之上:10 中常见 LLM 推理系统总结

留存学习&#xff0c;比较全面的ai训练的总结了。 以下文章来源于AI闲谈 &#xff0c;作者AI闲谈 一、背景 当前常见的 LLM 推理框架通常是在单模型、同构硬件资源、均匀流量分布和有限序列长度等条件下优化其 Serving 能力。在这种情况下&#xff0c;各种分布式并行方案、量…

【精品方案】大型企业数字化转型管控平台解决方案(55页PPT),干货满满!

引言&#xff1a;随着信息技术的飞速发展&#xff0c;数字化转型已成为大型企业提升竞争力、实现持续创新的必经之路。本解决方案旨在为企业提供一套全面、高效、灵活的数字化转型管控平台&#xff0c;助力企业实现业务流程的数字化重构、数据资产的智能管理以及决策过程的精准…

拒绝看烂书,弯道超车,2024年最新网络安全最应该看的书籍

学习的方法有很多种&#xff0c;看书就是一种不错的方法&#xff0c;但为什么总有人说&#xff1a;“看书是学不会技术的”。 其实就是书籍没选对&#xff0c;看的书不好&#xff0c;你学不下去是很正常的。 一本好书其实不亚于一套好的视频教程&#xff0c;尤其是经典的好书…

解锁5G新营销:视频短信的优势与全方位推广策略

随着5G时代的全面来临&#xff0c;企业的数字化转型步伐日益加快&#xff0c;视频短信作为新兴的数字营销工具&#xff0c;正逐步展现出其巨大的潜力。视频短信群发以其独特的形式和内容&#xff0c;将图片、文字、视频、声音融为一体&#xff0c;为用户带来全新的直观感受&…

10大wordpress外贸主题

手动工具wordpress外贸模板 适合生产套筒扳、管钳、工具箱、斧子、锤子、防爆工具、螺丝刀、扳手等手动工具的厂家。 https://www.jianzhanpress.com/?p4806 Invisible Trade WP外贸网站模板 WordPress Invisible Trade外贸网站模板&#xff0c;做进出口贸易公司官网的word…

Linux iptables使用详解

一、Linux系统下使用iptables 在Linux中&#xff0c;常用的防火墙工具是iptables。以下是一些基本的iptables命令&#xff0c;用于配置防火墙规则。 查看现有的iptables规则&#xff1a; sudo iptables -L 清除所有现有的规则&#xff08;慎用&#xff0c;可能导致服务不可用…

Springboot的小型超市商品展销系统-计算机毕业设计源码01635

摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作…

激动人心的LayerDiffusion终于可以在ComfyUI中使用了

一、什么是LayerDiffusion 随着Stable Diffusion等散射模型的蓬勃发展,人工智能图形生成进入了一个崭新的阶段。我们可以仅仅通过文字提示,就可以让AI模型为我们生成逼真的图像。但是,目前主流的AI生成模型大多只能生成普通的RGB图像,对生成具有透明通道的图片能力还非常有限。…

list集合自定义排序

一、基本类型排序 1.list中只有数字或字符串 //升序排序 List<T> ,T为数字或字符串 Collections.sort(list); //降序排序 Collections.sort(list,Collections.reverseOrder());2.list中为对象 基于jdk.18 import lombok.Data;Data public class User {private int i…

LangChain 与 Elastic 合作为 RAG 添加向量数据库和语义重排序

作者&#xff1a;来自 Elastic Max Jakob 在过去的一年中&#xff0c;我们看到了生成式人工智能领域的许多进展。许多新服务和库应运而生。LangChain 已成为使用大型语言模型 (LLM) 构建应用程序的最受欢迎的库&#xff0c;例如检索增强生成 (RAG) 系统。该库使原型设计和试验不…

blender bpy将顶点颜色转换为UV纹理vertex color to texture

一、关于环境 安装blender的bpy&#xff0c;不需要额外再安装blender软件。在python控制台中直接输入pip install bpy即可。 二、关于代码 本文所给出代码仅为参考&#xff0c;禁止转载和引用&#xff0c;仅供个人学习。 本文所给出的例子是https://download.csdn.net/downl…

iOS界面设计要点:四大模块解析

UI设计不是艺术设计&#xff0c;这限制了我们从设备和现有技术开始设计。因此&#xff0c;熟悉每个平台的设计规则已经成为每个设计师的第一课&#xff0c;也是每个设计师必要的专业知识。 今天小边给您带来了iOS设计规范&#xff0c;希望帮助您快速熟悉iOS平台设计规范&#…

搜索与图论:染色法判别二分图

搜索与图论&#xff1a;染色法判别二分图 题目描述参考代码 题目描述 输入样例 4 4 1 3 1 4 2 3 2 4输出样例 Yes参考代码 #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N 100010, M 200010;int n, m; i…

JAVA小知识16:JAVA常用的API

一、Math 方法名说明public static int abs(int a)获取参数绝对值public static double ceil(double a)向上取整public static double floor(double a)向下取整public static int round(float a)四舍五入public static int max(int a,int b)获取两个int值中的较大值public s…

基于springboot实现车辆管理系统项目【项目源码+论文说明】

基于springboot实现车辆管理系统演示 摘要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。以前企业对于车辆信息的管理和控制&#xff0c;采用人工登记的方式保存相关数据&#xff0c;这种以…

Turbo Console Log自定义配置

写log太麻烦了&#xff1f;可以用下vscode中的Turbo Console Log的插件 因为vscode的其他快捷键可能会和这个插件产生冲突&#xff0c;所以可以从这里设置自定义不重复的快捷键。我这里用的shiftaltG用来生成log 我用的是显示第多少行和路径名 效果&#xff1a; 还有其他的…

Atlas基于云器Lakehouse升级数据平台,实现业务效率与平台稳定性的双重提升

导读 Atlas 是一家富有创新精神的新加坡旅游科技初创公司&#xff0c;由连续创业企业家 Mary 及其团队于 2019 年底成立。公司利用互联网技术高效聚合和分发全球廉价航空公司的特价机票&#xff0c;服务于全球旅游业生态系统。技术部门需要与包括航空公司、在线旅行社&#xf…

深度神经网络——语音识别技术的探索与应用

概述 论文地址&#xff1a;https://arxiv.org/pdf/2402.19443.pdf 使用深度学习的语音识别技术已取得重大进展。这使得语音识别系统更加准确。然而&#xff0c;这项技术非常复杂&#xff0c;很难理解哪些信息用于何处。因此&#xff0c;本文提出了一种识别语音识别系统中哪些信…

pycharm基本使用(常用快捷键)

0.下载 pycharm官网下载 选择合适的版本&#xff0c;本文以2024.1为例 1.简单应用 常用快捷键 ctrlD 复制当前行 ctrlY 删除当前行 ctrlX 剪切当前行&#xff08;可用作删除&#xff0c;更顺手&#xff09; shift↑ 选中多行ctrlshiftF10 运行 shiftF9 调试ctrl/ 注释当前…