都说synchronized锁jdk1.6之前很慢,1.6以后优化的已经很快了,今天看一下到底怎么个快法了,优化了哪些呢?
大概意思就是jdk1.6以前加锁就是重量级锁之后又引入了偏向锁、轻量级锁,从而总共有了四种锁状态,由低到高为:无锁状态-》偏向锁状态-》轻量级锁状态-》重量级锁状态
1、无锁状态
没有对资源进行锁定,所有线程都能访问公共资源
2、偏向锁
一段同步代码块,不存在多线程竞争,而是一直被同一线程多次获取,从而引入了偏向锁,来降低锁的开销
加锁:加锁时进行一次CAS记录该线程的id,后面只需要判断是不是同一个线程就行,不需要重复CAS动作了
释放锁:此时A线程拥有偏向锁,线程B进入时,需要等待全局安全点(时间点无字节码执行),然后暂停拥有偏向锁的线程,检查拥有偏向锁的线程是否存活,如果挂了,直接置为无锁状态,然后B线程加锁操作,如果处于活动状态,检查该对象对偏向锁的使用情况,如果不使用,直接置为无锁状态,B线程加锁操作,如果还在使用,那么进入到轻量级锁状态
3、轻量级锁
线程B处于自旋等待占用线程A释放锁,自旋一定次数后发生锁升级(锁膨胀),升级为重量级锁;当A线程持有轻量级锁,B线程在自旋等待,C线程也来竞争锁时会升级为重量级锁
升级轻量级锁有两种途径
1、关闭偏向锁时,直接进入轻量级锁
2、A线程持有偏向锁,B线程来竞争锁时会升级为轻量级锁
4、重量级锁
重量级锁就是将争抢锁的线程从用户态转变为内核态,让cpu借助操作系统来协调线程运行,当A线程持有锁时,其它线程全部阻塞等待,直到A线程释放锁后其它线程再竞争(每次只有一个线程)
当A线程持有轻量级锁,B线程在自旋等待,C线程也来竞争锁时会升级为重量级锁
整体流程
- 线程A进入通过一次CAS加锁,持有偏向锁
- 线程B进入与线程A发生锁竞争,产生锁升级,升级为轻量级锁,此时A持有锁,B自旋等待锁
- 线程C进入与B同时等待,此时锁升级,进入重量级锁,此时A持有锁,B、C处于等待状态
- A释放锁后B、C进入锁竞争
整个状态只能存在升级即:无锁-》偏向锁-》轻量级锁-》重量级锁;不存在降级
三种锁的优缺点:
备注:
CAS简介:
需要输入两个值,一个旧的一个新的,先查找出来跟旧值比较,如果相等则替换为新值,如果不相等重复此操作
优点:通过自旋不断竞争锁,不需要线程状态切换(用户态和内核态切换)
可能引发问题:
- 自旋次数多耗时较长时比较耗CPU
- ABA问题,通过新增字段版本号解决
- 只能保证一个共享变量的原子操作,jdk1.5后新增了AtomicReference可将多个变量放入对象
公众号主要记录各种源码、面试题、微服务技术栈,帮忙关注一波,非常感谢