如何使用 Redis 实现分布式锁?(附教学视频)

redis 实现分布式锁

redis 实现分布式锁的方式有两种:

通过 redis 提供的 setnx 进行实现,往 redis 中使用 setnx 插入 key 时,如果 key 存在,则返回 0,可以通过插入 key 的返回值进行判断来实现分布式锁

通过使用 Redission(客户端)来实现分布式锁。可以调用 Redission 提供的 api,即 lock(),unlock()方法进行加锁和锁的释放。此外, Redission 还提供了 watchdog,即看门狗,来对加锁的 key 每隔 10 s对该 key 进行续时,(从而避免锁的过期)

Redission 的所有指令是通过 lua 脚本进行实现的,lua 脚本可以保证所有指令执行的原子性

关键词:setnx,Redission,lock,unlock,watchdog,lua,原子性

基于 Redis 的分布式锁实现思路

下面是使用 Redis 实现分布式锁的步骤:

生成分布式锁的 key,一般为业务相关的业务 key;

生成分布式锁的 value,一般为1或者线程 id;

生成分布式锁的过期时间,避免锁过程中宕机锁无法解开;

通过不同语言的 API 实现 SET key value [EX seconds] [NX]命令;

通过命令执行结果返回的标识判断是否加锁成功,一般 true 为上锁成功;

释放锁时,通过删除分布式锁的 key 实现。

Java 实现

这里提供不可重入的分布式锁的实现方式:

@Component
public class RedisLock {

    @Resource
    private StringRedisTemplate redisTemplate;

    /**
     * 尝试获取分布式锁
     * @param key 锁的键值
     * @param value 锁的值
     * @param expireTime 锁的过期时间
     * @param timeUnit 锁的过期时间单位
     * @return 是否获取到锁
     */
    public boolean tryLock(String key, String value, long expireTime, TimeUnit timeUnit) {
        // 利用 setIfAbsent 方法实现分布式锁
        Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, timeUnit);
        return Boolean.isTrue(success);
    }

    /**
     * 释放分布式锁
     * @param key 锁的键值
     */
    public void unlock(String key) {
        redisTemplate.delete(key);
    }
}

什么是可重入锁

分布式锁的可重入是指在分布式环境下,同一个线程可以多次获取同一个锁,而不会出现死锁或其他异常情况。这是因为可重入锁会记录当前线程已经获取锁的次数,每次释放锁时会将次数减一,只有当次数为零时才会真正释放锁。在分布式环境下,可重入锁会将当前线程的标识和获取锁的次数一起存储在分布式存储系统中,以便其他节点可以识别当前线程是否已经获取了锁,并且可以正确地释放锁。

可重入锁的好处是可以避免死锁和其他异常情况,同时也可以提高代码的可读性和可维护性。但是,在分布式环境下,可重入锁的实现需要考虑到网络延迟、节点故障等因素,因此需要谨慎设计和测试。

可重入锁的简单实现

下面是一个使用Java和Redis实现可重入分布式锁的示例代码:

import redis.clients.jedis.Jedis;

public class RedisReentrantLock {
    private Jedis jedis;
    private String lockKey;
    private String lockValue;
    private int lockCount;

    public RedisReentrantLock(Jedis jedis, String lockKey) {
        this.jedis = jedis;
        this.lockKey = lockKey;
        this.lockValue = null;
        this.lockCount = 0;
    }

    public synchronized boolean lock() {
        if (lockCount > 0) {
            lockCount++;
            return true;
        }
        String value = String.valueOf(System.currentTimeMillis());
        if (jedis.setnx(lockKey, value) == 1) {
            lockValue = value;
            lockCount = 1;
            return true;
        } else {
            String currentValue = jedis.get(lockKey);
            if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) {
                String oldValue = jedis.getSet(lockKey, value);
                if (oldValue != null && oldValue.equals(currentValue)) {
                    lockValue = value;
                    lockCount = 1;
                    return true;
                }
            }
        }
        return false;
    }

    public synchronized boolean unlock() {
        if (lockCount == 0) {
            return false;
        }
        lockCount--;
        if (lockCount == 0) {
            jedis.del(lockKey);
            lockValue = null;
        }
        return true;
    }
}

在这个示例中,我们使用了Jedis客户端来连接Redis服务器。构造函数接受一个Jedis实例和一个锁的键值作为参数。lock()方法用于获取锁,如果当前线程已经获取了锁,则增加锁的计数器,否则尝试使用setnx命令在Redis中创建一个新的键值对,如果创建成功,则表示当前线程获取了锁,否则尝试获取当前锁的值并比较是否过期,如果过期则使用getset命令更新锁的值,如果更新成功则表示当前线程获取了锁。unlock()方法用于释放锁,如果当前线程还持有锁,则减少锁的计数器,如果计数器为零,则删除锁的键值对。

需要注意的是,这个示例中的锁并不是完全可重入的,因为它只记录了锁的计数器,而没有记录获取锁的线程。如果多个线程使用相同的锁键值并且在同一时间尝试获取锁,则可能会出现死锁或其他异常情况。为了实现完全可重入的锁,需要在锁的值中记录获取锁的线程标识,并在释放锁时检查当前线程是否持有锁。

Redisson实现分布式锁

Redisson是一个基于Redis的Java驻留对象服务(Remote Service)和分布式锁框架。它提供了一种简单而强大的方式来实现分布式锁,以确保在分布式环境中的多个进程或线程之间的互斥访问。

下面是使用Redisson实现分布式锁的步骤:

  1. 引入Redisson依赖

在Maven项目中,可以通过以下方式引入Redisson依赖:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.5</version>
</dependency>
  1. 创建Redisson客户端

在使用Redisson之前,需要先创建Redisson客户端。可以通过以下方式创建:

Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);

这里使用的是单节点模式,如果是集群模式,需要使用useClusterServers()方法。

  1. 获取分布式锁

获取分布式锁的方式非常简单,只需要调用getLock()方法即可:

RLock lock = redisson.getLock("myLock");
lock.lock();
try {
    // 执行业务逻辑
} finally {
    lock.unlock();
}

这里创建了一个名为myLock的锁,并通过lock()方法获取锁。在执行业务逻辑之前,需要先获取锁,否则会被阻塞。

  1. 释放分布式锁

在业务逻辑执行完毕后,需要释放分布式锁,否则其他进程或线程无法获取锁。可以通过unlock()方法释放锁:

lock.unlock();

总结:

使用Redisson实现分布式锁非常简单,只需要引入依赖、创建Redisson客户端、获取锁、释放锁即可。同时,Redisson还提供了很多其他的分布式服务,如分布式Map、分布式List等,可以方便地实现分布式应用。

教学视频

我之前也录制了一套redis分布式锁的视频(https://www.imooc.com/learn/1371),点击下方阅读全文即可观看啦。

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

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

相关文章

自学网络安全(黑客),一般人我劝你还是算了吧

一、自学网络安全学习的误区和陷阱 1.不要试图先成为一名程序员&#xff08;以编程为基础的学习&#xff09;再开始学习 我在之前的回答中&#xff0c;我都一再强调不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;而且…

torch.distributed.launch多卡多机

torch.distributed.launch命令介绍 我们在训练分布式时候&#xff0c;会使用到 torch.distributed.launch 可以通过命令&#xff0c;来打印该模块提供的可选参数 python -m torch.distributed.launch --help usage: launch.py [-h] [--nnodes NNODES] [--node_rank NODE_RANK]…

诚迈科技携智达诚远出席高通汽车技术与合作峰会

5月25日至26日&#xff0c;诚迈科技及旗下的智能汽车操作系统及中间件产品提供商智达诚远作为高通生态伙伴&#xff0c;亮相首届“高通汽车技术与合作峰会”&#xff0c;通过产品展示和主题演讲呈现了基于高通骁龙数字底盘的最新智能座舱技术成果&#xff0c;共同展望智能网联汽…

GcExcel v6.1 支持新的 ‘.sjs‘ 模板文件 ‘.xltx‘ 格式 Crack

GrapeCity Documents for Excel (GcExcel) v6.1 版本现已上线&#xff01;该版本支持新的 SpreadJS .sjs 文件格式和 Excel 模板文件 .xltx 格式。此外&#xff0c;GcExcel 支持更多的SpreadJS兼容性功能和对 GcDataViewer 的多项增强。看看下面的主要亮点。 导入/导出 Spread…

Revit幕墙:用幕墙巧做屋面瓦及如何快速幕墙?

一、Revit中用幕墙巧做屋面瓦 屋面瓦重复性很高&#xff0c;我们如何快速的创建呢?下面我们来学会快速用幕墙来创建屋面瓦的技巧。 1.新建“公制轮廓-竖挺”族&#xff0c;以此来创建瓦的族(以便于载入项目中使用) 2.在轮廓族中绘制瓦的轮廓(轮廓需要闭合)&#xff0c;将族名称…

【JavaSE】Java基础语法(三十四):实现多线程

文章目录 1. 简单了解多线程2. 并发和并行3. 进程和线程4. 实现多线程方式一&#xff1a;继承Thread类【应用】5. 实现多线程方式二&#xff1a;实现Runnable接口【应用】6. 实现多线程方式三: 实现Callable接口【应用】7. 设置和获取线程名称【应用】8. 线程休眠【应用】9. 线…

Z-Library2023现状

网上基本上年年都会传出来Z-Library要被干掉的消息&#xff0c;我一直觉得&#xff0c;如果那真的发生了&#xff0c;会是人类的悲哀。 由于之前我存储的地址又挂了&#xff0c;所以紧急又寻找了一下。 1.朋友帮忙 朋友帮我搜了一下&#xff0c;发现有三个地址。 他说这第一个…

xlsx是什么格式

xlsx是什么格式? xlsx是Excel文档的扩展名&#xff0c;其基于Office Open XML标准的压缩文件格式&#xff0c;取代了其以前专有的默认文件格式&#xff0c;在传统的文件名扩展名后面添加了字母x&#xff0c;即.xlsx取代.xls。 xlsx文件是什么格式? xlsx是Excel表格的文件格…

【P34】JMeter ForEach控制器(ForEach Controller)

文章目录 一、ForEach控制器&#xff08;ForEach Controller&#xff09;参数说明二、准备工作三、测试计划设计 一、ForEach控制器&#xff08;ForEach Controller&#xff09;参数说明 可以对一个组变量进行循环迭代&#xff1b;该组件通常与后置处理器中的 JSON 提取器、正…

桥梁结构健康监测解决方案

城市桥梁担负着城市的交通和运输网络的重要角色&#xff0c;是城市生命线的重要组成部分。然而&#xff0c;随着时间的推移和日益增长的负荷&#xff0c;桥梁可能会受到各种因素的损害&#xff0c;如自然灾害、疲劳、腐蚀等。因此&#xff0c;桥梁结构健康监测变得至关重要&…

chatgpt赋能Python-python中怎么导入numpy

介绍 Python是一种广泛使用的编程语言&#xff0c;具有许多内建功能和模块&#xff0c;让开发者能够快速地编写代码。然而&#xff0c;虽然能够实现许多计算&#xff0c;但是原始Python本身并不足够处理各种科学和数字计算上需要的高效性&#xff0c;因此numpy这个开源的Pytho…

【机器学习】采样方法

文章目录 采样方法11.1 简介11.2 常见采样方法11.2.1 均匀分布采样11.2.2 逆变换采样11.2.3 拒绝采样11.2.4 重要采样11.2.5 Metropolis方法11.2.6 Metropolis-Hasting 算法11.2.7 吉布斯采样 采样方法 11.1 简介 什么是采样 从一个分布中生成一批服从该分布的样本&#xff0c…

JavaWeb ( 十 ) SpringMVC

4.Spring MVC Spring MVC是Spring提供的一个实现了Web MVC设计模式的轻量级Web框架。 三层架构分为表述层&#xff08;或表示层)、业务逻辑层、数据访问层&#xff0c;表述层表示前台页面和后台servlet 4.1.Spring MVC优点&#xff1a; ① 基于原生的Servlet&#xff0c;通过…

API测试| 了解API接口测试| API接口测试指南

什么是API&#xff1f; API是一个缩写&#xff0c;它代表了一个 pplication P AGC软件覆盖整个房间。API是用于构建软件应用程序的一组例程&#xff0c;协议和工具。API指定一个软件程序应如何与其他软件程序进行交互。 例行程序&#xff1a;执行特定任务的程序。例程也称为过…

MKS SERVO4257D 闭环步进电机_系列1 产品简介

第1部分 产品概述 1.1 产品介绍 MKS SERVO 28D/35D/42D/57D 系列闭环步进电机是创客基地为满足市场需求而自主研发的一款产品。具备脉冲接口&#xff0c;RS485接口和CAN接口&#xff0c;内置高效FOC矢量算法&#xff0c;采用高精度编码器&#xff0c;通过位置反馈&#xff0c;有…

【工具】vscode的常用插件之git插件

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;善假于物&#…

进程间通信(命名管道)

目录&#xff1a; 1.命名管道 2.创建命名管道 --------------------------------------------------------------------------------------------------------------------------------- 1.命名管道 1.管道的一个应用限制就是只能在具有共同祖先&#xff08;具有亲缘关系&…

网络编程初识

如果这篇有没接触过的知识点&#xff0c;请转到网络编程先导知识_小梁今天敲代码了吗的博客-CSDN博客 目录 IPv4和IPv6的概念&#xff1a; 子网掩码 默认网关 ping命令 端口 OSI网络分层模型 TCP/IP四层模型 字节序转换函数 IP地址转换 上一篇介绍了网络编程的先导知…

2023五一赶制个人系统:基于SpringBoot+MyBatisPlus+Vue+ElementUI前后端分离

小钊记前言 &#x1f351;一、背景&#x1f351;二、调研准备阶段&#x1f34a;2.1、项目-自己搭建&#x1f353; 搭建步骤 &#x1f34a;2.2、项目需求-自己X造&#x1f34a;2.2、数据模型设计 &#x1f351;三、开发阶段&#x1f351;四、renxiaozhao 1.0.0-alpha发布&#x…

SpringBoot配置文件的注入和读取

目录 1. 配置文件的作用 2. 两种配置文件的格式&#xff1a; 2.1 properties 基本语法&#xff1a; 2.1.1 写入 2.1.2 读取 执行原理 2.1.3 缺点分析 2.2 yml 基本语法&#xff1a; 2.2.1 写入&#xff08;非对象&#xff09; 2.2.3 配置对象 2.2.4 配置集合 多个配…