30分钟彻底弄懂 synchronized 锁升级过程( 三 )

通过在控制台中的打印内容我们可以发现,锁的状态一共经历了以下几个变化步骤:

  • biasable状态
在这个状态下,锁是一个带偏向的状态,此时如果有线程请求的话 。不过如果是刚启动JVM的状态的话,对象头部会是non-biasable状态,只有等jvm预热了一段时间(大约是4秒),才会留转变为biasable状态 。
  • biased状态
当第一个请求获取到锁的时候,锁的状态会变成偏向锁状态,也就是biased 。如果在处于偏向锁状态的时候,还有新的线程参与锁的抢夺,那么就会发生锁的升级,进入到轻量级锁状态阶段 。
  • thin lock状态
可以看到,当一个锁已经经历过偏向锁状态之后,后去如果再有其他线程访问它,它就会升级为轻量级锁的状态,也就是thin lock状态 。
  • fat lock状态
当我们在同步代码块中调用hashcode方法的时候,会发现,锁的对象头部会多出一个叫做fat lock的状态,这就意味着,此时该锁已经升级为了重量级锁的状态了 。
重量级锁当一把锁已经处于轻量级锁的状态时,如果此时又有多个线程来尝试获取锁,那么锁就会被多个线程以自旋的方式来请求访问,当访问的次数达到一定上限之后,synchronized就会自动升级为重量级锁的状态了 。
当升级为重量级锁的情况下,锁对象的mark word中的指针不再指向线程栈中的lock record,而是指向堆中与锁对象关联的monitor对象 。当多个线程同时访问同步代码时,这些线程会先尝试获取当前锁对象对应的monitor的所有权:
  • 获取成功,判断当前线程是不是重入,如果是重入那么recursions+1
  • 获取失败,当前线程会被阻塞,等待其他线程解锁后被唤醒,再次竞争锁对象
在重量级锁的情况下,加解锁的过程涉及到操作系统的Mutex Lock进行互斥操作,线程间的调度和线程的状态变更过程需要在用户态和核心态之间进行切换,会导致消耗大量的cpu资源,导致性能降低 。
有哪几种方式可以使一把锁升级为重量级状态?
  • 调用wait方法
  • 在同步代码块中调用对象的hashcode方法
最后我绘制了一张锁升级的流程图和大家分享下:
30分钟彻底弄懂 synchronized 锁升级过程

文章插图
 
小结其实JVM已经对synchronized进行了优化 。可以直接用,至于锁的力度如何,JVM底层已经做好了我们直接用就行 。不过作为一名工程师,了解这些底层原理还是可以增加我们自身内部的功力的 。
原文链接:
https://mp.weixin.qq.com/s/LsV9HKl8AW4vL4rz8iigxw

【30分钟彻底弄懂 synchronized 锁升级过程】


推荐阅读