2020年第一季度全球数据中心收入358亿美元
|
解锁代码执行方式与加锁类似,只不过解锁的执行结果返回类型使用 Long。这里之所以没有跟加锁一样使用 Boolean ,这是因为解锁 lua 脚本中,三个返回值含义如下:
如果返回值使用 Boolean,Spring-data-redis 进行类型转换时将会把 null 转为 false,这就会影响我们逻辑判断,所以返回类型只好使用 Long。
以下代码来自 JedisScriptReturnConverter: “如果 KEYS:[lock],ARGV[1000,uuid] 不熟悉 lua 语言同学也不要怕,上述逻辑还是比较简单的。 加锁代码首先使用 Redis exists 命令判断当前 lock 这个锁是否存在。 如果锁不存在的话,直接使用 hincrby创建一个键为 lock hash 表,并且为 Hash 表中键为 uuid 初始化为 0,然后再次加 1,最后再设置过期时间。 如果当前锁存在,则使用 hexists判断当前 lock 对应的 hash 表中是否存在 uuid 这个键,如果存在,再次使用 hincrby 加 1,最后再次设置过期时间。 最后如果上述两个逻辑都不符合,直接返回。
加锁代码如下: 释放锁的时首先判断重入次数,若大于 1,则代表该锁是被该线程拥有,所以直接将锁重入次数减 1 即可。 若当前可重入次数小于等于 1,首先移除 Map中锁对应的 key,然后再到 Redis 释放锁。 这里需要注意的是,当锁未被该线程拥有,直接解锁,可重入次数也是小于等于 1 ,这次可能无法直接解锁成功。 “ThreadLocal 使用过程要记得及时清理内部存储实例变量,防止发生内存泄漏,上下文数据串用等问题。下次咱来聊聊最近使用 ThreadLocal 写的 Bug。 相关问题 使用 ThreadLocal 这种本地记录重入次数,虽然真的简单高效,但是也存在一些问题。 过期时间问题 上述加锁的代码可以看到,重入加锁时,仅仅对本地计数加 1 而已。这样可能就会导致一种情况,由于业务执行过长,Redis 已经过期释放锁。 而再次重入加锁时,由于本地还存在数据,认为锁还在被持有,这就不符合实际情况。 如果要在本地增加过期时间,还需要考虑本地与 Redis 过期时间一致性的,代码就会变得很复杂。 不同线程/进程可重入问题 狭义上可重入性应该只是对于同一线程的可重入,但是实际业务可能需要不同的应用线程之间可以重入同把锁。 而 ThreadLocal的方案仅仅只能满足同一线程重入,无法解决不同线程/进程之间重入问题。 不同线程/进程重入问题就需要使用下述方案 Redis Hash 方案解决。 基于 Redis Hash 可重入锁 实现方式 ThreadLocal 的方案中我们使用了 Map 记载锁的可重入次数,而 Redis 也同样提供了 Hash (哈希表)这种可以存储键值对数据结构。所以我们可以使用 Redis Hash 存储的锁的重入次数,然后利用 lua 脚本判断逻辑。
加锁的 lua 脚本如下: (编辑:潍坊站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

