Java封装同步锁(完整版)
最后更新于
这有帮助吗?
最后更新于
这有帮助吗?
大家好这是一个普通的 controller
然后呢这一段代码被加了一个同步锁
意味着同一时刻
只有一个线程可以执行进来
但是呢这样有点问题
我们想要的效果是
同一个订单才会加一个同步锁
不同的订单之间我们不需要同步(执行)
他可以并行
那我们先来试一下现在的效果哈
我们来一个不同的订单
一个是1、一个是2
大家看只有1结束了2才开始
那有的同学就会想到
我们可不可以直接把 orderId 放进来
我们来试一下
一个1、一个2
大家看
哎可以同时开始了
但是我们再换一个
我们用两个1
清一下(控制台)。我们来试一下
大家看他两个1是同时开始的
原因是啊这个 synchronized 的这里边
比较的并不是字符串的值
而是说这个对象
那很显然
每一次请求进来的这个对象
一定不是同一个对象
所以呢解决这个问题
我们可以在这加一个intern
也就是说:把这个字符串的值
放到字符串常量池里面
这样的话呢他每次是同一个对象
我们来试一下
先
我们用两个1 大家看啊
大家看!这个是锁住了,两个1没问题
然后呢我们再用一个1、一个2
一个1 一个2注意看
这1和2是同时开始的,也就是说
这样写的话
效果是满足了我们想要的效果
但是呢这个写法又不是很推荐
具体的原因我们后边在讨论哈
那所以除了这个写法
我们还可以有另一种写法
就是我们在这写一个Map
然后呢自己来缓存一些锁
我们就叫mutexCache
我们先用 HashMap
然后呢
我们在这尝试从这个缓存里取一把锁
这是 gaito 的 id
我们就叫 mochas
four key
然后呢
肯定要判断这个 mutex4Key 是不是空
如果为空的话
mutexKey 等于 new 一个
然后再把这个 mutex 放进去
缓存里边
orderId
然后 mutex4Key
然后呢我们在这
用这个 mutex4Key 来作为锁
最终呢我们这里边要把它释放掉
mutexCache.remove
orderId 把这个锁释放掉
但是呢正常来说这应该放到 try ... finally
我们先这样写哈
然后我们再来试一下
好我们先来试一下
两个001
好注意看
好锁住了对吧
第一个结束了第二个才开始
我们再试来
001、002
一个1、一个2
大家看
这两个是同时开始的
也同时结束的
也说看上去这样就可以了
但是
他还是有问题
为什么呢因为
这一段代码没有加同步锁
那意味着极端情况下同一个订单的
两个线程可能会同时执行这段代码
那第一个稍微快一点刚执行到这
第二个也去取了
也就是说:第一个还没放进去
第二个一取他还是取不到锁
我们先来验证一下这个情况哈
我把这再清一下
那这样的话我们就不能用终端了
因为我的手的速度赶不上
所以呢我们可以借助这个jmeter工具
我把这个
请求都写好了
都是请求001
然后呢用100个并发来试一下
100个并发没有问题
把它停一下
还有(没执行完的),还有我们重启一下服务哈
好
我们用1,000个并发
注意看
1,000个并发也没有没有问题
我们再来一遍
再增加
1,000个没问题我们用5,000个
大家看
他就已经有很多线程是同时开始的了
就说在5,000个并发的时候
他就锁不住同一个订单了
那我们再来优化一下哈
我先把这个停一下
然后呢我们把程序优化一下
我们把这加一个线程锁
这回用this就行了
我们不管他是不是同一订单都要
同步执行这段代码
啊这个作用域(不对)
好再来启动一遍
然后呢我把jmeter再启动起来
好现在是5,000个并发
然后这个请求都是001
我们再来试一遍哈
大家看!它就不会再出现那种情况了
把它停掉哈
还是得停一会
服务停下
我还是把它强退吧
那我们针对这段代码其实还可以
有另一种写法
就是说我们把这个 map 换成
ConcurrentHashMap
这个是线程安全的
然后注意
这一段代码我们可以这样写
点 computeIfAbsent
然后呢把 orderId 拿过来
如果值不存在的话 我们new一个
new一个 Object 就好了
然后呢把它放到这个 mutex4key
也就是说:这一段代码的作用
和这个逻辑是一样的
我们拿 key 去取值
取不到的话new一个
而且这个 ConcurrentHashMap
是现场安全的
(所以)这一句代码不需要加同步锁了
所以呢我们就可以这样把这段去掉
好再来试一下
我们这边用1万个注意看啊
开始
大家看!他也不会有
问题也是一个一个来的
好,我再停一下
那到这里呢我们把功能就实现了
但是呢考虑到公用性
我们可以把它进一步封装一下
就说我们可以封装这么一个工具类
然后呢
准备这么一个 map
然后在这里边写一个方法叫 exec
让他传一个 runnable 进来
这样的话还是刚才那个代码断
只不过把这个 runnable
放在这执行一下
然后最终它替我们
管理这些锁
那使用起来呢就是我们把这个类
首先注入到这个 controller 里边
然后呢我们用这个来实现
把这些
代替掉
点 exec
然后 orderId
然后呢写一个箭头函数
这样
然后把这个逻辑放在这里边来执行
这样就好了
这个就不需要了
这些都不需要了
好这样就可以了
再见