Redis 作为一个独立的三方系统(通常被作为缓存中间件使用),其天生的优势就是可以作为一个分布式系统来使用,因此使用 Redis 实现的锁都是分布式锁,如下图所示:

在 Redis 中实现分布式锁可以使用 SETNX 和 EXPIRE 命令来实现,SETNX 是 "SET if Not eXists" 的缩写,是一个原子性操作,用于在指定的 key 不存在时设置 key 的值。如果 key 已经存在,SETNX 操作将不做任何事情,返回失败;如果 key 不存在,SETNX 操作会设置 key 的值,并返回成功。而 EXPIRE 是设置锁的过期时间的,主要为了防止死锁的发生,SETNX + EXPIRE 的实现命令如下:

其中“nx”表示 not exists 不存在则设置 key,“ex 10”表示过期时间为 10 秒,“mylock”值为 key,“lock”值为 value。

分布式锁问题

SETNX 和 EXPIRE 一起使用可以实现分布式锁的功能,但存在锁误删的问题,比如线程 1 设置的过期时间为 5s,而线程 1 执行了 7s,那么在第 5s 之后锁过期了,那么其他线程就可以拥有这把锁了,之后线程 1 执行完业务,又执行了锁删除操作,那么此时锁就被误删了。

解决方案

此时可以每个锁的 value 中添加拥有者的标识,删除之前先判断是否是自己的锁,如果是则删除,否则不删除。但是判断和删除之间不是原子性操作,所以依然有问题。此时可以使用 lua 脚本来判断并删除锁,lua 脚本可以保证 redis 中多条语句执行的原子性,所以就可以解决此问题了。

PS:如果觉得自己实现 lua 脚本比较麻烦,可以使用 Redisson 框架来实现分布式锁。它通过简单 API 可以实现分布式锁,并且没有上述问题,它的底层也是通过 lua 脚本来实现的,只不过框架已经帮开发者封装好了,这样开发者就可以把更多精力放在业务上,而无序担心分布式锁的操作问题了。


以上内容来自我的 《Java 面试突击训练营》,这门课程是 有着 14 年工作经验(前 360 开发工程师),9 年面试官经验的我,花费 4 年时间打磨完成的一门视频面试课

整个课程从 Java 基础到微服务 Spring Cloud、从实际开发问题到场景题应有尽有,如下图所示:

全程通过视频直播 + 录播的方式,把 Java 常见的面试题系统的过一遍,遇到一个问题,把这个问题相关的内容都给大家讲明白,并且视频支持永久更新和观看。

上完训练营的课程之后,基本可以应对目前市面上绝大部分公司的面试了,想要了解详情,加我微信:GG_Stone【备注:训练营】