为什么用于调用方法的 Java 字节码隐式获取和释放监视器?

2022-09-03 15:42:31

我一直在阅读Java虚拟机指令集,并注意到当使用指令调用标记为同步的方法(例如,invokestatic,invokevirtual等)时,由该特定字节码指令来获取接收器对象上的监视器。同样,从方法返回时,由离开方法的指令决定,以便在方法同步时释放监视器。这似乎很奇怪,因为有显式的监视器入口和监视器退出字节码来管理监视器。JVM 以这种方式设计这些指令,而不仅仅是编译方法以在适当的情况下包含 monitorenter 和 monitorexit 指令,这有什么特殊原因吗?


答案 1

早在90年代中期,还没有Java JIT编译器,微同步被认为是一个非常好的主意。

所以你经常称这些同步方法。甚至有'他们!您可以在没有额外字节码的情况下进行处理。Vector

但不仅仅是在运行代码时。类文件更大。额外的说明,但也设置/表并验证顽皮的东西没有被溜进去。tryfinally

只是我的猜测。


答案 2

你是在问为什么有两种方法可以做同样的事情吗?

当一种方法作为同步市场时,同时具有监视器进入/退出指令将是多余的。如果它只有monitorenter/exit指令,你将无法打赌能够从外部看到该方法已同步(无需读取实际代码)

有两种或多种执行相同操作方法的示例不胜枚举。每个都有相对的优点和缺点。(例如,许多单字节指令是两字节指令的短版本)

编辑:我一定在问题中遗漏了一些东西,因为调用方不需要知道被叫方是否同步

public static void main(String... args) {
    print();
    printSynchronized();
    printSynchronizedInternally();
}

public static void print() {
    System.out.println("not synchronized");
}

public static synchronized void printSynchronized() {
    System.out.println("synchronized");
}

public static  void printSynchronizedInternally() {
    synchronized(Class.class) {
        System.out.println("synchronized internally");
    }
}

生成代码

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method print:()V
   3:   invokestatic    #3; //Method printSynchronized:()V
   6:   invokestatic    #4; //Method printSynchronizedInternally:()V
   9:   return

public static void print();
  Code:
   0:   getstatic   #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc #6; //String not synchronized
   5:   invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

public static synchronized void printSynchronized();
  Code:
   0:   getstatic   #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc #8; //String synchronized
   5:   invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

public static void printSynchronizedInternally();
  Code:
   0:   ldc_w   #9; //class java/lang/Class
   3:   dup
   4:   astore_0
   5:   monitorenter
   6:   getstatic   #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   9:   ldc #10; //String synchronized internally
   11:  invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  aload_0
   15:  monitorexit
   16:  goto    24
   19:  astore_1
   20:  aload_0
   21:  monitorexit
   22:  aload_1
   23:  athrow
   24:  return
  Exception table:
   from   to  target type
     6    16    19   any
    19    22    19   any

}

推荐