说明
在JDK 1.8中,synchronized 的速度已经有了显著的提升,主要通过分级锁的方式。JVM 会根据使用情况对synchronized锁升级,会如此路径升级:偏向锁 -->轻量级锁--> 重量级锁。
锁信息
那锁是通过什么方式判断锁是否要升级呢?关键信息在对象头里,对象分为MarkWord、Class Pointer、Instance Data、Padding,其中MarkWord跟锁信息有关,它的长度是 24 位,它包含Thread ID(23bit)、Age(6bit)、Biased(1bit)、Tag(2bit) 四个部分,锁升级就是靠 Thread Id、Biased、Tag 等变量值来进行判断
分级类型
偏向锁
在只有一个线程使用了锁的情况下,偏向锁能够保证更高的效率。具体过程是这样的:
1)当第一个线程第一次访问同步块时,会先检测对象头 Mark Word 中的标志位 Tag 是否为 01,以此判断此时对象锁是否处于无锁状态或者偏向锁状态(匿名偏向锁)。
2)01是锁默认的状态,线程一旦获取了这把锁,就会把自己的线程ID写到MarkWord 中,在其他线程来获取这把锁之前,锁都处于偏向锁状态。
3)当下一个线程参与到偏向锁竞争时,会先判断 MarkWord 中保存的线程ID是否与这个线程 ID 相等,如果不相等就会立即撤销偏向锁,升级为轻量级锁。
轻量级锁
轻量级锁的获取是通过自旋方式。参与竞争的每个线程,会在自己的线程栈中生成一个 LockRecord ( LR ),然后每个线程通过 CAS(自旋)的方式,将锁对象头中的 MarkWord 设置为指向自己的 LR 的指针,哪个线程设置成功,就意味着哪个线程获得锁。当锁处于轻量级锁的状态时,就不能够再通过简单地对比 Tag 的值进行判断,每次对锁的获取,都需要通过自旋。当然,自旋也是面向不存在锁竞争的场景,比如一个线程运行完了,另外一个线程去获取这把锁;但如果自旋失败达到一定的次数,锁就会膨胀为重量级锁。
重量级锁
重量级锁,线程会挂起,进入到操作系统内核态,等待操作系统的调度,然后再映射回用户态。系统调用是昂贵的,所以重量级锁的名称由此而来。如果系统的共享变量竞争非常激烈,锁会迅速膨胀到重量级锁,这些优化就名存实亡。如果并发非常严重,可以通过参数 -XX:-UseBiasedLocking 禁用偏向锁,理论上会有一些性能提升,但实际上并不确定。
锁只能升级,不能降级
参考资料:拉钩《Java 性能优化实战 21 讲》
458

被折叠的 条评论
为什么被折叠?



