Monitor中线程状态的切换是通过什么实现的呢?首先线程状态从根源来说,也只是一个参数而已。其次,据我所知,Hotspot的Monitor是通过park()/unpark()实现(我看到的两份资料都是这么写的)。然后,Hostpot的Monitor中的park()/unpark()区别于JDK提供的park()/unpark(),两者完全不是一个东西。但是落地到操作系统层,可能是同一个东西。最后,这方面我了解得还不是很深入,如果有谁了解,欢迎交流。
锁的变迁最后就是,无锁,偏向锁,轻量级锁,重量级锁之间的转换了。
啥都别说了,上图。
这个图,基本就说了七七八八了。我就不再深入阐述了。
注意一点,轻量级锁降级,不会降级为偏向锁,而是直接降级为无锁状态。
重量级锁,就不用我说了。要么上锁,要么没有锁。
锁的优化锁的优化,包括自旋锁,自适应自旋锁,锁消除,锁粗化。
自旋锁(multiple core CPU)许多情况下,共享数据的锁定状态持续时间较短,切换线程不值得(也许切换线程的资源消耗就超过了共享数据的锁定持续时间带来的资源消耗)。
通过线程执行忙循环等待锁的释放,不让出CPU。
缺点:若锁被其它线程长时间占用,会带来许多性能上的开销。
自旋的等待时间是有限制的(其中忙循环的循环次数是存在默认值的)。
Hotspot可通过PreBlockSpin参数,修改默认旋转次数。
自适应自旋锁自旋的次数难以把握,难以完美。
自旋的次数不再固定(可能为零)。
由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。
举例:同一个锁对象上,自旋等待刚刚成功获取过锁,并且持有锁的线程正在运行=》JVM认为该锁自旋获得锁的可能性大。
锁消除JIT(Just In Time)编译时,对运行上下文进行扫描,去除不可能存在竞争的锁。
JIT(Hotspot Code):
运行频繁的代码,将会进行编译,转换为机器码。
JIT编译是以method为单位的。
锁粗化通过扩大加锁的范围,避免反复加锁和解锁。
总结刨除代码,这篇文章在已发表的文章中,应该是我花的时间最长,手打内容最多的文章了。
从开始编写,到编写完成,前前后后,横跨两个月。当然主要也是因为这段时间太忙了,没空进行博客的编写。
在编写这篇博客的过程中,我自己也收获很多,将许多原先自己认为自己懂的内容纠正了出来,也将自己对JVM的认识深度,再推进一层。
最后,愿与诸君共进步。
参考资料《深入理解Java虚拟机》
java中的各种数据类型在内存中存储的方式:
Java句柄
关于java数组的内存分配,顺便提一下java变量的内存分布
JVM 系列 - 内存区域 - Java 虚拟机栈(三)
Java对象头详解
java 偏向锁、轻量级锁及重量级锁synchronized原理
Java并发——Synchronized关键字和锁升级,详细分析偏向锁和轻量级锁的升级
Java锁---偏向锁、轻量级锁、自旋锁、重量级锁