Synchronized关键字底层实现原理

最近在看周志明的《深入理解JVM虚拟机》,看到class文件结构,便想边看边做些笔记。我们都知道Java“一次编译,到处运行”的特性,那么这种特性怎么实现的呢?Java源文件被Java编译器编译成JVM可以识别的.class文件执行,即JVM是Java程序运行的底层环境,而JVM虚拟机与操作系统没有什么关系,这就是“一次编译,到处运行”的原因。

但是JVM是怎么执行我们的.class文件的呢?JVM只负责执行.class文件,换言之如果其它语言经编译为.class文件,我们的JVM也是可以执行,其实当前已经有一些语言可以在JVM上执行,如JRuby、Groovy等。所以JVM即有跨平台又有跨语言的特性

class字节码文件由无符号数组成:

  • 无符号数:基本数据类型(如u1、u2、u4、u8代表对应数量的字节的无符号数),可以用来描述数字、索引引用、数量值或字符串值。
  • :多个无符号数的集合。(结尾带_info的一般为表)

Class文件格式:

类型 名称 数量 描述
u4 magic 1 魔数:0xCAFEBABE,用来确定这是一个class文件
u2 minor_version 1 Java的次版本号
u2 major_version 1 Java的主版本号,高版本可以兼容低版本,低版本不能兼容高版本,如JDK1.7的版本号最大值是51
u2 constant_pool_count 1 常量池容量计数值,唯一的计数从1开始的容量计数
cp_info constant_pool constant_pool_count-1 常量池:主要放字面量(Literal)和符号引用(Symbolic References)。符号引用:类和接口的全限定名(Fully Qualified Name), 字段的名称和描述符(Descriptor),方法的名称和描述符
u2 access_flags 1 访问标志,用于识别一些类或者接口层次的访问信息。为类还是接口、是否为public类型、是否定义为static等
u2 this_class 1 类索引
u2 super_class 1 父类索引,只有一个u2,Java单一继承的体现
u2 interfaces_count 1 接口索引计数值,Java接口多实现的表现
u2 interfaces interfaces_count 接口索引
u2 fields_count 1 字段表计数值
field_info fields fields_count 字段表:用于描述接口或者类中声明的变量
u2 methods_count 1 方法表计数值
method_info methods methods_count 方法表:记录方法的访问标志(access_flags)、名称索引(name_index)、描述符索引(descriptor)、属性表集合(attributes)
u2 attributes_count 1 属性表计数值
attribute_info attributes attributes_count 属性表:在class文件、字段表、方法表都可以携带自己的属性表集合,用于描述某些场景专有的信息

方法表的access_flags(注意其中的):
在这里插入图片描述

JVM通过字节码指令去执行class类文件,JVM的指令有:
加载和存储指令、运算指令、类型转换指令、对象创建与访问指令、操作数栈管理指令、控制转移指令、方法调用和返回指令、异常处理指令、同步指令。

无疑与Synchronized相关的就是同步指令了:
Synchronized可以修饰方法和代码块,也可以修饰静态方法和Class。这两种实现都使用了管程Monitor,JVM会给所有class文件的对象和类加一个Monitor。

方法级的同步是隐式的,无需通过字节码指令来控制。这个意思就是只要在方法前加了Synchronized来修饰,就会在方法表的访问标志(access_flags)的SYN_CHRONIZED标志位置为0x0020。当调用该方法时,调用指令会先检SYN_CHRONIZED,如果标志了执行线程首先会看是否有其它线程执有该对象管程Monitor,如果有(Monitor>0)就阻塞等待知道其它线程执行完方法释放管程(Monitor=0),然后才能去持有该对象的管程(Monitor++)。

代码块的同步是通过字节码指令monitorenter和monitorexit来实现的,正确的实现需要Javac编译器和Java虚拟机共同协作支持。

猜你喜欢

转载自blog.csdn.net/weixin_40682142/article/details/88386445