Redis--黑马点评项目分布式锁释放存在的问题
以上这张图描述了这样一种场景:线程1在获取分布式锁后进入业务阻塞状态,并且阻塞时间甚至超过了我们设置的锁过期时间。在此期间,线程2成功获取了锁并开始执行业务,此时被阻塞的线程1又恢复运行并完成了业务,于是按照代码逻辑线程1会去释放锁,此时另一个线程3又来获取锁,造成了线程安全问题。
出现该问题的本质原因在于在编写锁的释放代码时,并没有添加判断条件,这样做的漏洞就是释放的锁不一定是自己的锁。解决的方案也很简单,只需要在释放锁之前判断是否是自己的锁。可以使用UUID+线程id作为判断的标识,需要注意的是这种标识依旧无法做到完全唯一。如果想要实现标识完全唯一,可以使用每台电脑的MAC+线程id作为标识。本项目使用的是第一种方案
1 | private static final String LOCK_PREFIX = UUID.randomUUID().toString(true); |
在某些极端情况下,当前代码还会出现问题。
上图表示的情景是线程1判断锁标识后想要执行下一步时,突然发生的阻塞,导致锁无法正常释放,因此又发生了线程安全问题。出现该问题的原因在于,编写释放锁的代码时,判断和释放是两个动作,不能保证操作的原子性。
实现Redis操作原子性的一种方案为使用Lua语言,这是一种脚本语言,并且可以使用该语言对Redis进行操作。以下网站是一个关于Lua语言的学习网站。
1 | --利用lua脚本实现redis语句的原子性 |
1 | //redis中提供的操作脚本语言的类 |
通过本节需要掌握:
- 在释放锁时需要考虑这个锁是否是自己的锁
- 学会使用lua语言保证执行redis语句的的原子性
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 dch'blog!