Redis 分布式锁实现详解

news/2024/10/11 0:18:39/

Redis 分布式锁实现详解

分布式系统中,我们需要解决的一个重要问题是多个服务实例之间如何协调共享资源的访问问题。例如,在电子商务系统中,库存更新需要被多个微服务实例所共享,但为了防止超卖,必须确保库存更新是原子性的。这就是分布式锁发挥作用的地方。

本文将介绍如何使用 Redis 实现一个简单的分布式锁,并提供完整的 Java 代码示例。

1. 前置知识

  • Redis:一个开源的键值存储系统,支持多种数据结构,如字符串、哈希表等。
  • Jedis:一个用于 Redis 的 Java 客户端库。
  • Java:本示例使用 Java 编程语言。

2. 设计思路

2.1 锁的获取与释放

我们可以通过 Redis 的 SET 命令来实现锁的获取和释放:

  • 使用 SETNX(Set if Not eXists)命令来尝试获取锁。
  • 使用 EXPIRE 命令为锁设置过期时间,以避免死锁。
  • 使用 DEL 命令来释放锁。

2.2 锁的重入性

为了避免线程 A 获取了锁之后,线程 B 冒充线程 A 来释放锁,我们可以给每个线程分配一个唯一的标识符,这个标识符可以是线程 ID 或者是 UUID。这样,在释放锁的时候,需要检查锁的持有者是否是当前线程。

2.3 锁的自动释放

如果获取锁的客户端异常退出或者网络中断,锁应该能够自动释放。为此,我们需要给锁设置一个过期时间。

3. 代码实现

3.1 添加依赖

首先,确保你的项目中有 Jedis 的依赖。如果你使用 Maven,可以在 pom.xml 文件中添加以下依赖:

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.10.0</version>
</dependency>

3.2 RedisDistributedLock 类

接下来,我们将创建一个 RedisDistributedLock 类来实现上述逻辑。

import redis.clients.jedis.Jedis;public class RedisDistributedLock {private final Jedis jedis;private final String lockKey;private final int lockTimeoutSeconds;private String identifier;public RedisDistributedLock(Jedis jedis, String lockKey, int lockTimeoutSeconds) {this.jedis = jedis;this.lockKey = lockKey;this.lockTimeoutSeconds = lockTimeoutSeconds;}public boolean tryLock() {identifier = UUID.randomUUID().toString();Long result = jedis.setnx(lockKey, identifier);if (result == 1L) {// Set the lock timeout to avoid deadlocksjedis.expire(lockKey, lockTimeoutSeconds);return true;}return false;}public void unlock() {String currentValue = jedis.get(lockKey);if (currentValue != null && currentValue.equals(identifier)) {jedis.del(lockKey);}}
}

3.3 测试代码

下面是一个简单的测试类来验证我们的锁机制是否有效。

public class LockTest {public static void main(String[] args) {Jedis jedis = new Jedis("localhost", 6379);RedisDistributedLock lock = new RedisDistributedLock(jedis, "test_lock", 10);Thread thread1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + " is trying to acquire the lock.");if (lock.tryLock()) {try {System.out.println(Thread.currentThread().getName() + " has acquired the lock.");Thread.sleep(5000); // Simulate some work} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();System.out.println(Thread.currentThread().getName() + " has released the lock.");}} else {System.out.println(Thread.currentThread().getName() + " failed to acquire the lock.");}});Thread thread2 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + " is trying to acquire the lock.");if (lock.tryLock()) {try {System.out.println(Thread.currentThread().getName() + " has acquired the lock.");Thread.sleep(5000); // Simulate some work} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();System.out.println(Thread.currentThread().getName() + " has released the lock.");}} else {System.out.println(Thread.currentThread().getName() + " failed to acquire the lock.");}});thread1.start();thread2.start();}
}

这段代码创建了两个线程,试图同时获取锁。运行该程序,你会看到只有一个线程成功获取了锁,而另一个线程则无法获取锁。

4. 总结

通过上述实现,我们已经构建了一个基本的 Redis 分布式锁。在实际应用中,你可能还需要考虑更多的场景和细节,比如锁的续期、异常处理等。希望这篇博客能为你提供一个良好的起点!


http://www.ppmy.cn/news/1506779.html

相关文章

复现一下最近学习的漏洞(sqlab 1-10)

第一个问题&#xff1a;为什么不能用#来闭合单引号呢&#xff1f; 在进行URL地址栏传参的时候&#xff0c;是有一套编码规范的。他不会编码英文、数字和某些符号。但是#它会进行编码。也就是%23。&#xff08;先转ascii码&#xff0c;然后再转十六进制&#xff0c;之后加上%就是…

20240810设置倒立字图片的方法

20240810设置倒立字图片的方法 2024/8/10 20:38 缘起&#xff1a;有些时候&#xff0c;为了/需要达到某些效果。需要将字倒立。 比较简单的方法&#xff1a; 百度&#xff1a;在线 倒着写 WPS 倒立文字 1、最简单粗暴的。在记事本/WPS中输入文字之后&#xff0c;使用微信/QQ截…

编程-设计模式 2:抽象工厂模式

设计模式 2&#xff1a;抽象工厂模式 定义与目的 定义&#xff1a;抽象工厂模式提供一个接口&#xff0c;用于创建一系列相关或相互依赖的对象&#xff0c;而无需指定它们具体的类。目的&#xff1a;该模式的主要目的是解耦客户端代码与产品类之间的关系&#xff0c;并确保一…

【Linux:进程优先级】

什么叫做优先级&#xff1a; 指进程获取某种资源的顺序 在linux中&#xff0c;进程是以struck_task进行描述的&#xff0c;他的本质就是一个结构体&#xff0c;该结构体中含有很多个内部字段&#xff0c;优先级就属于该结构体中的某个字段。此外&#xff0c;在Linux环境下&am…

新160个crackme - 027-MexeliteCRK1

运行分析 需破解Serial和Status PE分析 Delphi程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 ida搜索字符串&#xff0c;找到关键字符串 先静态分析&#xff0c;结果如上注释 比较Serial和’Benadryl’字符串&#xff0c;相等弹出成功 验证成功

第二十二天学习笔记2024.8.6

同步时间 yum -y install ntpdate.x86_64 ntpdate ntp.ntsc.ac.cn 两台主机都要安装 libaio 和rsync yum -y install rsync rpm -qa |grep libaio查看是否安装 没有就安装 然后安装mysql8.0的安装包解压 [rootmsater ~]# tar -xvf mysql-8.0.33-linux-glibc2.12-x86_64.tar …

服务器CPU架构有几种?分别应用到什么场景?有啥优缺点?

服务器CPU架构主要有以下几种&#xff1a; 1. x86 架构 应用场景&#xff1a; 企业数据中心&#xff1a;广泛用于企业级服务器&#xff0c;如Web服务器、数据库服务器、虚拟化服务器等。高性能计算&#xff08;HPC&#xff09;&#xff1a;用于科学计算、工程模拟等需要强大计…

token续签方案

在处理 Token 的有效期续签时&#xff0c;通常有两种主要策略&#xff1a;自动续签和手动续签。以下是实现这些策略的一些建议&#xff1a; 1. 自动续签 这种方法通常通过使用刷新 Token 来实现&#xff1a; 颁发两个 Token&#xff1a; 访问 Token&#xff08;Access Token…