加入收藏 | 设为首页 | 会员中心 | 我要投稿 潍坊站长网 (https://www.0536zz.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长资讯 > 动态 > 正文

2020年第一季度全球数据中心收入358亿美元

发布时间:2021-02-21 16:59:18 所属栏目:动态 来源:互联网
导读:解锁代码执行方式与加锁类似,只不过解锁的执行结果返回类型使用 Long。这里之所以没有跟加锁一样使用 Boolean ,这是因为解锁 lua 脚本中,三个返回值含义如下: 1 代表解锁成功,锁被释放 0 代表可重入次数被减 1 null 代表其他线程尝试解锁,解锁失败 如果

解锁代码执行方式与加锁类似,只不过解锁的执行结果返回类型使用 Long。这里之所以没有跟加锁一样使用 Boolean ,这是因为解锁 lua 脚本中,三个返回值含义如下:

  • 1 代表解锁成功,锁被释放
  • 0 代表可重入次数被减 1
  • null 代表其他线程尝试解锁,解锁失败

如果返回值使用 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 脚本如下:


(编辑:潍坊站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!