上级目录

概览

从 Instruction 的角度来说,与 monitor 相关的 opcode 有 2 个,内容如下:

opcode mnemonic symbol opcode mnemonic symbol opcode mnemonic symbol opcode mnemonic symbol
194 monitorenter 195 monitorexit        

从 ASM 的角度来说,这些 opcode 与 MethodVisitor.visitXxxInsn() 方法对应关系如下:

  • MethodVisitor.visitInsn(): monitorenter, monitorexit

示例

从 Java 语言的视角,有一个 HelloWorld 类,代码如下:

public class HelloWorld {
    public void test() {
        synchronized (System.out) {
            System.out.println("Hello World");
        }
    }
}

从 Instruction 的视角来看,方法体对应的内容如下:

$ javap -c -p sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
  public void test();
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: dup
       4: astore_1
       5: monitorenter
       6: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       9: ldc           #3                  // String Hello World
      11: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      14: aload_1
      15: monitorexit
      16: goto          24
      19: astore_2
      20: aload_1
      21: monitorexit
      22: aload_2
      23: athrow
      24: return
    Exception table:
       from    to  target type
           6    16    19   any
          19    22    19   any
}

从 ASM 的视角来看,方法体对应的内容如下:

Label label0 = new Label();
Label label1 = new Label();
Label label2 = new Label();
Label label3 = new Label();
Label label4 = new Label();

methodVisitor.visitCode();
methodVisitor.visitTryCatchBlock(label0, label1, label2, null);
methodVisitor.visitTryCatchBlock(label2, label3, label2, null);

methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitInsn(DUP);
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitInsn(MONITORENTER);

methodVisitor.visitLabel(label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("Hello World");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitInsn(MONITOREXIT);

methodVisitor.visitLabel(label1);
methodVisitor.visitJumpInsn(GOTO, label4);

methodVisitor.visitLabel(label2);
methodVisitor.visitVarInsn(ASTORE, 2);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitInsn(MONITOREXIT);

methodVisitor.visitLabel(label3);
methodVisitor.visitVarInsn(ALOAD, 2);
methodVisitor.visitInsn(ATHROW);

methodVisitor.visitLabel(label4);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 3);
methodVisitor.visitEnd();

从 Frame 的视角来看,local variable 和 operand stack 的变化:

                               // {this} | {}
0000: getstatic       #2       // {this} | {PrintStream}
0003: dup                      // {this} | {PrintStream, PrintStream}
0004: astore_1                 // {this, PrintStream} | {PrintStream}
0005: monitorenter             // {this, PrintStream} | {}
0006: getstatic       #2       // {this, PrintStream} | {PrintStream}
0009: ldc             #3       // {this, PrintStream} | {PrintStream, String}
0011: invokevirtual   #4       // {this, PrintStream} | {}
0014: aload_1                  // {this, PrintStream} | {PrintStream}
0015: monitorexit              // {this, PrintStream} | {}
0016: goto            8        // {} | {}
                               // {this, Object} | {Throwable}
0019: astore_2                 // {this, Object, Throwable} | {}
0020: aload_1                  // {this, Object, Throwable} | {Object}
0021: monitorexit              // {this, Object, Throwable} | {}
0022: aload_2                  // {this, Object, Throwable} | {Throwable}
0023: athrow                   // {} | {}
                               // {this} | {}
0024: return                   // {} | {}

从 JVM 规范的角度来看,monitorenter 指令对应的 Operand Stack 的变化如下:

..., objectref →

...

The objectref must be of type reference.

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:

  • If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
  • If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
  • If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership.

从 JVM 规范的角度来看,monitorexit 指令对应的 Operand Stack 的变化如下:

..., objectref →

...

The objectref must be of type reference.

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.

The thread decrements the entry count of the monitor associated with objectref.

  • If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner.
  • Other threads that are blocking to enter the monitor are allowed to attempt to do so.