[讨论] Java synchronized是否有对应的字节码指令实现?
chainhou
2013-11-24
最近在看“深入java虚拟机”里面提到的class文件结构及其javac之后生成的字节码指令等。就想搞明白一下synchronized底层的实现方式。
测试类如下: import java.util.concurrent.atomic.AtomicInteger; public class Counter { static int count = 0; static AtomicInteger c = new AtomicInteger(); public synchronized int getNum() { return count++; } public int getNum0() { return count++; } public int getNWithAtomic() { return c.getAndIncrement(); } } 用javap命令得到如下结果: static int count; flags: ACC_STATIC static java.util.concurrent.atomic.AtomicInteger c; flags: ACC_STATIC static {}; flags: ACC_STATIC Code: stack=2, locals=0, args_size=0 0: iconst_0 1: putstatic #12 // Field count:I 4: new #14 // class java/util/concurrent/atom ic/AtomicInteger 7: dup 8: invokespecial #16 // Method java/util/concurrent/ato mic/AtomicInteger."<init>":()V 11: putstatic #19 // Field c:Ljava/util/concurrent/a tomic/AtomicInteger; 14: return LineNumberTable: line 7: 0 line 9: 4 line 5: 14 LocalVariableTable: Start Length Slot Name Signature public common.Counter(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #23 // Method java/lang/Object."<init> ":()V 4: return LineNumberTable: line 5: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcommon/Counter; public synchronized int getNum(); flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=3, locals=1, args_size=1 0: getstatic #12 // Field count:I 3: dup 4: iconst_1 5: iadd 6: putstatic #12 // Field count:I 9: ireturn LineNumberTable: line 11: 0 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcommon/Counter; public int getNum0(); flags: ACC_PUBLIC Code: stack=3, locals=1, args_size=1 0: getstatic #12 // Field count:I 3: dup 4: iconst_1 5: iadd 6: putstatic #12 // Field count:I 9: ireturn LineNumberTable: line 14: 0 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcommon/Counter; public int getNWithAtomic(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: getstatic #19 // Field c:Ljava/util/concurrent/a tomic/AtomicInteger; 3: invokevirtual #30 // Method java/util/concurrent/ato mic/AtomicInteger.getAndIncrement:()I 6: ireturn LineNumberTable: line 17: 0 LocalVariableTable: Start Length Slot Name Signature 0 7 0 this Lcommon/Counter; } 从javap的结果看,方法getNum和getNum0的字节码指令一样,都是先获取static字段之后常量1入栈,再存入static中,返回。对于是否包含synchronized关键字,并没有相应的字节码指令对应。此处是我测试不对呢,还是确实实现都是这样,只是在执行时再判断方法声明中是否包含synchronized关键字,再加以控制(类似于载入class文件时判断其方法或类声明是否为public这样)? 另外,对于atomicInteger的指令,在方法getNWithAtomic方法中,也是分两个指令来执行,先获取static,再执行其相应的increment方法,那atomic底层是有东西保证这两个指令是以原子方式执行的吗? |
|
runshine
2013-11-24
monitorenter
monitorexit |
|
chainhou
2013-11-24
@runshine,对是这样的,我在代码中加了一个方法:
Object o = new Object(); public void t() { synchronized(o) { System.out.println("aaa"); } } 这个时候javap的输出: public void t(); flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=1 0: aload_0 1: getfield #26 // Field o:Ljava/lang/Object; 4: dup 5: astore_1 6: monitorenter 7: getstatic #34 // Field java/lang/System.out:Ljav a/io/PrintStream; 10: ldc #40 // String aaa 12: invokevirtual #42 // Method java/io/PrintStream.prin tln:(Ljava/lang/String;)V 15: aload_1 16: monitorexit 17: goto 23 20: aload_1 21: monitorexit 22: athrow 23: return Exception table: from to target type 7 17 20 any 20 22 20 any 包含了monitorenter和monitorexit |
|
runshine
2013-11-24
对于AtomicInteger上你调用的getAndIncrement它调用了compareAndSet,compareAndSet调用了sun.misc.Unsafe的compareAndSwapInt方法
compareAndSwapInt是个native方法 一般现代的CPU都提供的有一个称为CAS的原子操作,compareAndSwapInt用的就是具体平台对应的实现。 具体参照这里http://baike.baidu.com/subview/18179/6777667.htm |
|
RednaxelaFX
2013-11-24
楼主的疑惑在《Oracle JRockit: The Definitive Guide》里有详细描述。刚顺手写了篇笔记发在豆瓣了:http://book.douban.com/annotation/29492994/
|
|
chainhou
2013-11-24
谢谢R大和runshine的回答,解释的很清楚。
|
|
ZHH2009
2014-07-28
我来补充一下:
synchronized的实现分两种,一种是synchronized方法,一种是synchronized块, 两种方法稍有不同,但是核心还是一样的。 synchronized方法就像R大的笔记中提到的, 并没有生成对应的monitorenter、monitorexit字节码, 而是通过方法的access_flags字段中的ACC_SYNCHRONIZED标志来识别, 带有ACC_SYNCHRONIZED标志的方法在HotSpot中会通过method_entry_point_zerolocals_synchronized来触发, 在实际执行方法字节码之前会根据UseHeavyMonitors和UseBiasedLocking参数来确定 先用哪种锁,然后在方法返回时再做相关的解锁操作(体现在return字节码的汇编代码中)。 synchronized块有对应的monitorenter、monitorexit字节码, monitorenter的汇编代码有一部分是跟method_entry_point_zerolocals_synchronized相同的,同样monitorexit的汇编代码有一部分也跟return的汇编代码相同。 具体的汇编代码这里有一点: https://github.com/codefollower/OpenJDK-Research/blob/master/hotspot/my-docs/interpreter/stub/method_entry_point_zerolocals_synchronized.java |
相关讨论
相关资源推荐
- ruby on rails中的session & cookies整理
- Rails Session工作原理
- iframe中session失效问题,rails应用与iis的解决方案
- IE下iframe跨域session和cookie失效问题的解决方案
- rails中使用flash变量启用消息提醒
- 【Ruby on Rails】2021 OWASP TOP 10 的安全加固建议
- Ruby on rails 实战圣经:Part 2: 深度剖析环境设定与Bundler
- 配置 Rails 应用程序
- rails on ruby,ruby on rails 之Action Dispatch
- Rails对请求的操作