# Java封装同步锁（二次翻车后续）

{% embed url="https://v.douyin.com/rks5S8g/" %}

{% embed url="https://www.bilibili.com/video/BV1KL4y1x7om/" %}

大家好上期视频又翻车

那翻车的原因很多小伙伴也指出来了啊

其实就是极端情况下

有些新进来的线程还是会在这里拿到

刚刚被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
