引:趁大家午饭时间给大家整个文,下个馒头、下碗米饭、溜个小酒、开个胃~\
程序员的技术文章前序
序
青松成山
蓝天白云砌墙阳光穿透竹涧
洒在青石台上
萦绕残蝉杂与鸟鸣声
昨夜雨滴含蓄点落凉亭
避开城市的喧嚣与燥热
开始文字与自然的交织些许一杯绿茶才配这夏日份清爽~
可爱猪猪的周末又被悄然打开:
美好的周末,从一篇好文章开始!
分布式锁的隐私太多了系列之
第二弹:是谁偷偷释放你的锁?
剧情回顾
上一弹,揭秘了:Redis之死锁三进宫
回顾:根据字节面试改编原创小说:分布式锁的隐私太多了系列之死锁三进宫
我们循序找到了setnx+del的加锁、释放锁解决分布式环境下访问共享资源线程安全的三个漏洞:
-
-
代码异常导致死锁
finally代码块释放锁保证异常也被触发锁释放\ -
进程意外终止导致死锁
设置key的超时时间,迫使key最终也被释放,避免死锁\ -
setnx与expire非原子性操作
采用Redis 2.6.12配备的原子命令:SET lock 1 EX 10 NX。似完美解决!
经过三次优化,伪代码是这样的: \
-
// 获取锁并设置过期时间为30秒
boolean success = setnxAndsetExpire(key,30);
try{
// 做一些不可描述的事情
doSomeBeyondDescription();
}finally{
// 释放锁
del(key);
}
是不是就这样就大结局了?!
Of Course,NO!!!
是谁偷偷释放了我们的锁?
引入多线程情况下,往往会增加程序开发与设计角度的复杂性。加锁操作也不例外。
两个线程或多个线程的之间通过共享资源通讯。我们通过下面纯手工绘图来看下会带来什么问题呢:
- 线程1加锁(把门锁好)并设置超时时间30秒(给自己预估了30秒),开始做不可描述的事情并持续1分钟,(线程1比较持久😋),但是明显给自己预估的时间少了,在30秒的时候锁释放(门自动打开了)。
- 线程2一直在等待拿锁,在30秒的时候,发现锁释放(
门打开了,线程1还在里面做事情),结果线程2加锁(把门也锁好),也开始做一些不可描述的事情。 - 线程1在2分30秒事情终于做完了,把锁释放了。(把门打开走了)
- 线程3一直在等待拿锁,发现锁被释放。(
门打开了,这时候线程2还在里面做事情) - 线程3也开始做事情了...
场面一度尴尬,并失控了:
- 30s的时候线程1和线程2在同时做事情 ----注:①
- 2min3s的时候线程2和线程3在同时做事情 ----注:②
那是谁在偷偷的释放我们的锁?你肯定有了答案。
是线程1
如何保证锁不被别人偷偷释放?
今天这篇文章我们先说下问题②,关注我们公众号「面试怪圈」,我们下篇会继续侃问题①!
不知道你有没有解决思路呢?
揭秘啦!揭秘啦!
每个线程使用自己的专属token。 通过使用专属token每个线程只能释放自己加的锁🔐。
伪代码是这样紫:
// 生成token
String token = UUID.random();
// 获取锁并设置过期时间为30秒,并给了一个专属token
boolean success = setnxAndsetExpire(key,token,30);
try{
// 做一些不可描述的事情
doSomeBeyondDescription();
}finally{
// 释放锁,忽略==和equals的区别
if(get(key) == token){
del(key);
}
}
每个线程加的锁只能自己释放,线程1再也不会打开线程2的门了。
是不是Redis分布式锁就此无可挑剔了?
答案又是否定的?分布式环境和多线程的背景下,实在实在实在是太复杂了!
注①中线程1和线程2同时访问的共享代码块的问题并没有解决。
好了,饭点了,该上菜了!
根据字节面试改编原创小说:分布式锁的隐私太多了系列第三弹,下篇待续....
咱们在「面试怪圈」继续摆,我是可爱猪猪。
走一个finally!
某猪说
某某: 猪你好危险呀?
猪猪: 为啥?
某某:你知道的太多了。
猪猪:😒 冷~
猪猪不断学习使自己不愚钝。
猪猪将毕生的学习资料整理成了个人资源库:
有业界经典款面试题、基础教程、高阶教程、大厂面经、中间件实践、数据库原理、技术学习路线、思维导图...
太多了,写不下去了~
都看到这里了,过来瞅瞅这只奋斗的猪猪吧:
www.mianshiguaiquan.com
打开是这个样紫,是你钟爱的简约小小小卡片风格吧: