synchronized 在 JDK 1.5 之前性能是比较低的,在那时我们通常会选择使用 Lock 来替代 synchronized。然而这个情况在 JDK 1.6 时就发生了改变,JDK 1.6 中对 synchronized 进行了各种优化,性能也得到了大幅的提升,这也是目前版本中还能经常见到 synchronized 身影的重要原因之一。当然除了性能之外,synchronized 的使用也非常便利,这也是它流行的重要原因。
在众多优化方案中,锁膨胀机制是提升 synchronized 性能最有利的手段之一(其他优化方案我们后面再讲),本文我们重点来看什么是锁膨胀?以及锁膨胀的各种细节。
正文
在 JDK 1.5 时,synchronized 需要调用监视器锁(Monitor)来实现,监视器锁本质上又是依赖于底层的操作系统的 Mutex Lock(互斥锁)实现的,互斥锁在进行释放和获取的时候,需要从用户态转换到内核态,这样就造成了很高的成本,也需要较长的执行时间,这种依赖于操作系统 Mutex Lock 实现的锁我们称之为“重量级锁”。
什么是用户态和内核态?
用户态(User Mode):当进程在执行用户自己的代码时,则称其处于用户运行态。内核态(Kernel Mode):当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态,此时处理器处于特权级最高的内核代码中执行。
为什么分内核态和用户态?
假设没有内核态和用户态之分,程序就可以随意读写硬件资源了,比如随意读写和分配内存,这样如果程序员一不小心将不适当的内容写到了不该写的地方,很可能就会导致系统崩溃。
而有了用户态和内核态的区分之后,程序在执行某个操作时会进行一系列的验证和检验之后,确认没问题之后才可以正常的操作资源,这样就不会担心一不小心就把系统搞坏的情况了,也就是有了内核态和用户态的区分之后可以让程序更加安全的运行,但同时两种形态的切换会导致一定的性能开销。
锁膨胀
在 JDK 1.6 时,为了解决获取锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”的状态,此时 synchronized 的状态总共有以下 4 种:
- 无锁
- 偏向锁
- 轻量级锁
- 重量级锁
锁的级别按照上述先后顺序依次升级,我们把这个升级的过程称之为“锁膨胀”。
“PS:到现在为止,锁的升级是单向的,也就是说只能从低到高升级(无锁 -> 偏向锁 -> 轻量锁锁 -> 重量级锁),不会出现锁降级的情况。
锁膨胀为什么能优化 synchronized 的性能?当我们了解了这些锁状态之后自然就会有答案,下面我们一起来看。