欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术的推送!
在我后台回复 「资料」 可领取
编程高频电子书
!
在我后台回复「面试」可领取硬核面试笔记
!文章导读地址:点击查看文章导读!
感谢你的关注!
Java核心能力
为什么不建议在高并发场景下使用 synchronized?
这首先我们要了解 高并发场景的特点
以及 synchronized 底层加锁的原理
是怎样的!
首先说一下 synchronized 底层加锁的原理:
synchronized 在 JDK1.6 之后引入了锁的优化,随着多线程竞争的激烈程度不同,使用的锁也不同
-
当没有线程竞争,此时为
无锁
状态 -
如果只有一个线程不停访问同步代码块,此时会使用
偏向锁
-
如果有两个以上线程并发访问,偏向锁会撤销,并升级为
轻量级锁
(偏向锁在 JDK15 之后就被废弃了,因为撤销带来性能开销比较大) -
如果在轻量锁 CAS 自旋达到一定次数还没有拿到锁,就会撤销轻量级锁,升级为
重量级锁
,其实重量级锁的开销是比较大的,因为底层涉及到
在高并发场景下,并发度肯定是比较高的,不建议使用 synchronized 的原因主要有以下几点:
- 由于并发度比较高,因此 synchronized 一定会升级到重量级锁,但是重量级锁的性能是不太高的,因为线程要阻塞再唤醒,需要用户态和内核态之间切换
- synchronized 没有读写锁优化
- synchronized 不能对线程唤醒,也就是你线程如果获取不到锁的话会一直阻塞
所以如果优化的话,对于第一个点来说,将等待线程阻塞再唤醒,个人感觉优化空间不大
第二个点就是读写锁的优化,读读之间不互斥,大幅度增强 读多写少
场景下的性能!
第三个点就是需要一个 tryLock(timeout)
功能,在指定时间获取不到锁的时候,可以直接将线程超时了,不去拿锁了
- 为什么说需要
tryLock(timeout)
这个功能呢?
假设这样一种场景,有一个任务在某个时间点可能多个线程同时要来执行,但是只要有一个线程执行完毕之后,其他线程就不需要执行了
那么假设在这个需要执行任务的时间点,大量线程同时过来执行,也就是大量线程都进入阻塞队列等待获取锁,第一个线程拿到锁执行任务之后,此时后边的线程都不需要执行该任务了,但是由于没有这个超时功能,导致后边的线程还需要在队列中等待获取锁,再一个个进入同步代码块,发现任务已经执行过了,不需要自己再执行了,之后再退出同步代码块
因此这个 tryLock(timeout)
的作用就是 将大量线程的串行操作转为并行操作 ,大量线程发现指定时间内获取不了锁了,直接超时,不获取锁了,这样后边的线程再来看就发现任务已经执行过了,不需要再去获取锁执行任务了
这里 tryLock(timeout)
的情况只是举一个特殊的情况,其实是参考了分布式环境下,更新 Redis 缓存时会出现这种情况,但是在分布式环境下肯定不会使用 synchronized ,因此这里主要是举个例子说一下 tryLock 的作用!
上边主要说了 synchronized 的缺点,一方面是为了应对面试,另一方面也可以通过各种问题来引发自己的思考,让自己对 synchronized 的理解更加深入
一般在写项目使用分布式锁还是多一些,毕竟高并发项目肯定不会使用单节点部署
而单机项目的话,一般也不会追求极致的性能,使用 synchronized 也没有什么问题