Redission实现的分布式锁的可重入性
Redisson 分布式锁在 Redis 中存储可重入状态所使用的 Hash 结构,并通过示例说明。
核心数据结构
- Key: 锁的名称。例如:
"myLock"
。 - 数据类型:
Hash
(Redis HSET / HGET / HINCRBY 操作的对象)。 - Hash Field (字段名): 客户端唯一标识符。格式通常为:
UUID:threadId
。UUID
: 生成 Redisson 客户端实例时创建的一个全局唯一 ID(一个 JVM 进程一个)。threadId
: 当前请求锁的线程 ID(Java 中的Thread.currentThread().getId()
)。- 作用: 精确标识是哪个 JVM 进程中的哪个线程持有锁。这是实现可重入的基础,同一个线程多次获取锁时,Field 相同。
- Hash Value (字段值): 一个整数,表示该线程对这把锁的 重入次数。
- 当线程第一次成功获取锁时,这个值被设置为
1
。 - 当同一个线程再次成功获取锁(重入)时,这个值会递增(
+1
)。 - 当线程释放锁时,这个值会递减(
-1
)。 - 只有当这个值减到
0
时,才表示该线程已经完全释放了这把锁,此时 Redis 会删除这个 Hash Key。
- 当线程第一次成功获取锁时,这个值被设置为
示例场景
假设:
- 锁名称 (Key):
"order_lock:1001"
(假设为处理订单 ID 1001 的锁) - 客户端 A 的 UUID:
"550e8400-e29b-41d4-a716-446655440000"
- 客户端 A 中线程 ID:
31
场景 1: 线程 31 第一次获取锁成功
- Redis 命令模拟 (Lua 脚本执行后的效果):
HSET order_lock:1001 550e8400-e29b-41d4-a716-446655440000:31 1 PEXPIRE order_lock:1001 30000 # 设置 30 秒过期时间
- Redis 中存储的数据:
Key: "order_lock:1001"Type: hashField: "550e8400-e29b-41d4-a716-446655440000:31" -> Value: 1 (整数)TTL: ~30000ms
- 解释: 线程
31
首次获取锁,重入次数为1
。
场景 2: 同一个线程 31 在锁内再次获取锁成功 (重入)
- 假设线程
31
在持有order_lock:1001
的代码块内,又调用了另一个需要获取同一把锁的方法。 - Redis 命令模拟 (Lua 脚本执行后的效果):
HINCRBY order_lock:1001 550e8400-e29b-41d4-a716-446655440000:31 1 # 值从1变成2 PEXPIRE order_lock:1001 30000 # 重置过期时间
- Redis 中存储的数据:
Key: "order_lock:1001"Type: hashField: "550e8400-e29b-41d4-a716-446655440000:31" -> Value: 2 (整数)TTL: ~30000ms (被重置)
- 解释: 同一个线程
31
重入锁,重入次数递增到2
。锁的过期时间被重置(看门狗或续期逻辑)。
场景 3: 线程 31 第一次释放锁 (重入锁)
- 线程
31
执行到外层锁的unlock()
。 - Redis 命令模拟 (Lua 脚本执行后的效果):
HINCRBY order_lock:1001 550e8400-e29b-41d4-a716-446655440000:31 -1 # 值从2变成1 PEXPIRE order_lock:1001 30000 # 重置过期时间 (因为计数 > 0)
- Redis 中存储的数据:
Key: "order_lock:1001"Type: hashField: "550e8400-e29b-41d4-a716-446655440000:31" -> Value: 1 (整数)TTL: ~30000ms
- 解释: 线程
31
释放了一次锁(对应第一次获取),重入次数减为1
。锁仍然被该线程持有(计数 > 0),所以 Key 没有被删除,并且过期时间被重置。
场景 4: 线程 31 第二次释放锁 (完全释放)
- 线程
31
执行到内层锁的unlock()
。 - Redis 命令模拟 (Lua 脚本执行后的效果):
HINCRBY order_lock:1001 550e8400-e29b-41d4-a716-446655440000:31 -1 # 值从1变成0 DEL order_lock:1001 # 因为计数减到0,删除Key PUBLISH redisson_lock__channel:{order_lock:1001} ... # 发布解锁消息通知等待者
- Redis 中存储的数据:
Key: "order_lock:1001" -> 已被删除
- 解释: 线程
31
释放了最后一次持有的锁(对应第二次获取),重入次数减为0
。此时 Redis 会删除整个 Keyorder_lock:1001
,并通过 PUBLISH 命令通知所有正在等待这把锁的其他客户端。
关键点总结
- 结构: 一个锁 Key (
String
) 对应一个 Hash 数据结构。 - 标识: Hash 的 Field 是
<ClientUUID>:<ThreadID>
,唯一标识持有锁的客户端和线程。 - 计数: Hash 的 Value 是一个 整数,记录该线程对这把锁的 重入次数。
- 原子操作: 加锁 (
HINCRBY ... 1
/HSET ... 1
)、解锁 (HINCRBY ... -1
)、检查持有者 (HEXISTS
)、删除锁 (DEL
) 等关键操作都封装在 Lua 脚本中执行,保证原子性。 - 可重入性: 同一个线程多次获取锁时,操作的是同一个 Hash Field,对其 Value 进行递增;释放时递减,直到为 0 才真正释放锁。这完美实现了可重入锁的语义。
通过这种 Hash 结构的设计,Redisson 高效、清晰地实现了分布式环境下锁的可重入性管理。