锁升级
1、无锁
25bit | 4bit | 1bit | 2bit |
---|---|---|---|
Hashcode | 分代年龄 | 0 | 01 |
2、偏向锁
23bit | 2bit | 4bit | 1bit | 2bit |
---|---|---|---|---|
线程ID | Epoch | 分代年龄 | 0 | 01 |
- 大部分情况下不存在锁竞争,常常是一个线程多次获得同一个锁
- 因为偏向锁不主动释放锁,如果下次再次获取锁,只需比较线程ID是否一致,如果一致,则可以直接获得锁,无需CAS加锁解锁
- 如果不一致,查看对象头中的线程ID是否还存活,如果已经不存活,则将对象重置为无锁状态,重新竞争将其设为偏向锁
- 如果对象头中的线程ID仍然存活,如果不再需要该偏向锁,则将对象重置为无锁状态,重新竞争将其设为偏向锁
- 如果对象头中的线程ID仍然存活,且还需要该偏向锁,则暂停该线程,撤销偏向锁,升级为轻量级锁
3、轻量级锁
30bit | 2bit |
---|---|
指向栈中所记录的指针 | 00 |
- 竞争锁的线程不多,且线程持有锁的时间也不长的情景,因为阻塞线程需要切换到内核态,代价较大,如果阻塞不久,锁就被释放,线程切换代价会很大
- 但是轻量级锁自旋是需要消耗CPU资源的,如果自旋时间过长,那么正常运算时间就会越少
- 轻量级锁升级为重量级锁
- 自旋次数到达限制
- 一个线程持有轻量级锁,一个线程自旋等待的时候,有一个线程竞争该对象的锁
4、重量级锁
30bit | 2bit |
---|---|
指向互斥量的指针 | 11 |
- 互斥量mutex需要操作系统的参与,即需要切入内核态,线程切换消耗较大,适用于竞争锁的线程持有锁的时间较长且线程数较多的情况
无锁->偏向锁->轻量级锁->重量级锁的过程称为锁升级
轻量级锁不能降级为偏向锁
重量级锁不能降级为轻量级锁
锁粗化
因为每次加锁、解锁都需要消耗资源,锁粗化就是将多个连续的加锁、解锁操作连接在一起,将其粗化为一个更大范围的锁,来避免频繁的加锁、解锁。
锁消除
在虚拟机JIT即时编译时,会进行逃逸分析,去除掉不必要的锁,节省掉加锁和解锁的开销。