Java封装同步锁(二次翻车后续)
最后更新于
这有帮助吗?
最后更新于
这有帮助吗?
大家好上期视频又翻车
那翻车的原因很多小伙伴也指出来了啊
其实就是极端情况下
有些新进来的线程还是会在这里拿到
刚刚被remove掉的一些锁
导致呢这个线程就会跟后边
再新进来的线程并发执行这个方法
那我们来试一下哈
今天就不用工具来请求了
因为
少量的请求试不出来
这回我们用20,000个线程来试
而且呢我试了一下用这个并行流
重现的概率很低
反而用这个普通的流
他重现的概率比较高
因为我们要测的情况属于
旧线程没跑完 新线程进来这种情况
所以这种流更合适
当然了这么写
其实也就相当于直接在这
Thread 的 star 就一样了
点 star
我们来2万个线程哈 大家看
好 搜一下什么呢
搜一下:“开始\n开始” 看!
这里面就会有很多
哎他这次只有一个我们再跑一遍
啊这回就多
就是说这种情况还是有 bug 吗
那怎么解决呢
我们看一下啊
那问题就是在他这拿到的锁
是刚刚被remove掉的锁
所以呢他再lock也能进来
但是呢他会跟后边再进来的线程(并发)
因为后边再进来线程拿不 (拿不到被remove的锁)
他就没有拿到
这个刚刚被remove掉的锁了
所以呢这两部分线程就会并发执行
那我们就可以在这判断一下
就是说我们锁住以后
再去这个cache里边
根据这个 key 拿一下
再拿到一把锁我们叫 mutexInCache
然后就有两种极端情况吗
第一种就是我刚刚拿到了这把锁然
后又从map去取的时候他就为空了
说明我刚刚拿到这把锁
被remove掉了
那第二种情况呢
就是我刚刚拿到这把锁
被新进来的线程因为都是001吗
又创建了一把新锁又覆盖了
所以呢他俩不等
不等于这个 inCache
只要满足这两种情况
其中任意一种就说明有问题
就不要往下走了
就要让他代码回到这一行重新去
取锁去!所以呢就是相当于这个
if 它 或者 它 有问题
接下来就有问题对吧让他回到上面
所以这个地方我们要用 do
然后呢 while
对吧!然后这个作用域往上调
那这个也是
好
但是呢大家注意这里边lock了一下
所以
当他再回去的时候一定要unlock
当然了你不能直接unlock
因为他第一次进来是空的
等于null
所以呢这块要判断一下他 not null
然后呢他点 unlock
这样就没有问题了
那有同学会说
会不会极端情况下发生在这样
又说满足了这个条件了
刚要执行这个 run 的时候
他就被remove
这个呢 我也琢磨了一下 是不会的啊
因为啊
他在这拿到的锁有三种情况哈
我们来画一下
就是他拿到这把锁有三种情况
第一种呢就是他自己创建了一把
就是说他一进来发现
原来这个001没有锁
那他自己创建了一把
那第二种情况呢就是
他拿到了一把
别人创建的
而且是正常的
有排队的线程
这种情况也是正常的
他直接加入到这个排队的行列里边了
第三种情况就不正常了
他拿到了一把
刚刚被人remove的(锁)
对吧前两种情况都是正常的
只有最后一种是不正常的
我们来看哈
那他拿到一把不正常的锁的时候呢
他走lock方法只要能走进来说明
被remove的那把锁已经被 unlock 了
否则他走不到这行代码对吧
那他走到这行代码以后
我们通过循环判断
哎这个锁不对劲
要么是remove了
要么是跟缓存里不一致了
就会让他回到解放前重新去拿锁
那最悲催的就是哈
这个线程运气特别差
他第二次进来又拿到了一把
刚刚remove的锁
那他继续进入到这个循环
他又回去
假设这个线程的运气就是特别差
他一直拿到的是这个被 remove 的锁
那他的命运就是
一直走到第2万个线程以后
没办法了他就自己创建了一把锁
所以就走完了
我们来试一下哈
咱们现在还是用2万个线程
开始,大家看 没有
多试几次
就这个我都已经试过了哈
没有找不到
我们再来一次啊
还是没有
好我们再来一次
还是没有好到这呢
之前的问题都解决了
但是呢今天还想给大家分享一个东西
因为这次翻车哈
很多小伙伴在评论区里说
说实话这个之前我也没用过哈
然后呢我就研究了一下这个 WeakHashMap是什么
我们来看一下哈
我这里写了一个 controller 然后呢
有一个全局的变量(属性)
就是一个 WeakHashMap
我们来看一下文档哈
然后呢把这个放到翻译
里不用翻译那么多
到我看
到这就行
他说这个是
基于hash表实现的一个弱键的一个 map
然后呢这里面的条目会在
这个键不常用的时候自动删除
也说这个 key 不常用
他就会自动删除什么叫不常用呢
其实就是
没有引用了
我们来试一下哈
我这里边不是准备了一个WeakHashMap吗
然后呢又准备了一个List专门
存一些引用的哈
然后
准备一个 put 的一个 controller
这里边很简单
我们就
接收到 order_id 之后 put 一个 new Object
同时哈注意看我把这个 new Object
放到List里面了
就是说我 new 的这个 Object
还是有引用存在的
即使这个 controller 走完了
因为放在这里了吗
但是这个 key 没有放起来
大家看,只要走完这个 controller 意味着
这个String的这个 key
就没有引用了
他就可以被垃圾回收了对吧
那按照刚才的那个文档里的意思是
就会在垃圾回收的时候清掉
然后呢我们再写一个 gc 方法
这个很简单
然后我们再写一个show
就是打印这个两个集合的
数量 哈,我们把它这个跑起来
然后借这个
功夫再给大家推荐一个这个
这是我刚发现的一个嗯
类似于 curl 的特别好用
正好一会给大家看一下
好启动起来了哈
好我们发起两个请求
这个 -qq 就是隐藏打印的
结果不用打印了
然后直接冒号端口号
这样的话他默认就请求 localhost
然后呢我们先 put 001
大家看现在是 weakHashmap size 是1
然后那个 list 也是1
我们再来一个2
2对吧
3
好我们就不整太多了啊
然后我我再来show一下
3对吧
好然后这时候执行gc一下
执行完 gc
他默认打印的时候大家看他还是3
我执行完 gc 我就打印了一下
但是哈他不能说明他没有 gc 掉
我们再调一下show
大家看 weakHashMap 已经是0了
那个List还是3
也就说明
这个WeakHashMap他只在
key 没有引用的时候他会自动清掉
那如果我们这个
map 换成 WeakHashMap
然后下面这块不做remove处理
会有什么情况出现呢
就是 这个 key
只要 controller 的走完他没有引用了
这个锁就会被自动的清掉了(gc的时候)
那就极端情况下他还是会
导致第一次这个翻车的 bug
所以呢
咱们这个场景还不能用WeakHashMap