锁升级

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即时编译时,会进行逃逸分析,去除掉不必要的锁,节省掉加锁和解锁的开销。