public class SyncTest { public void syncBlock(){ synchronized (this){ System.out.println("hello block"); } } public synchronized void syncMethod(){ System.out.println("hello method"); } }点击复制代码复制出错复制成功
当SyncTest.java被编译成class文件的时候,synchronized
关键字和synchronized
方法的字节码略有不同,我们可以用javap -v
命令查看class文件对应的JVM字节码信息,部分信息如下:
{ public void syncBlock(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter // monitorenter指令进入同步块 4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 7: ldc #3 // String hello block 9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 12: aload_1 13: monitorexit // monitorexit指令退出同步块 14: goto 22 17: astore_2 18: aload_1 19: monitorexit // monitorexit指令退出同步块 20: aload_2 21: athrow 22: return Exception table: from to target type 4 14 17 any 17 20 17 any public synchronized void syncMethod(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED //添加了ACC_SYNCHRONIZED标记 Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #5 // String hello method 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }点击复制代码复制出错复制成功
对于synchronized
关键字而言,javac
在编译时,会生成对应的monitorenter
和monitorexit
指令分别对应synchronized
同步块的进入和退出,有两个monitorexit
指令的原因是为了保证抛异常的情况下也能释放锁,所以javac
为同步代码块添加了一个隐式的try-finally,在finally中会调用monitorexit
命令释放锁。
而对于synchronized
方法而言,javac
为其生成了一个ACC_SYNCHRONIZED
关键字,在JVM进行方法调用时,发现调用的方法被ACC_SYNCHRONIZED
修饰,则会先尝试获得锁。
synchronized锁执行流程图
这是网上看到的一个流程图:
就是Java对象的内存布局其实由对象头+实例数据+对齐填充三部分组成,而对象头主要包含Mark Word+指向对象所属的类的指针组成。Mark Word主要用于存储对象自身的运行时数据,哈希码,GC分代年龄,锁标志等。
下面就是Mark Word的数据映射表