The following Java code generates the following JVM bytecode.
I'm curious why the code from offset 31 to offset 36 is generated. Nothing in the JLS7 or JVM7 specification talks about this. Did I miss anything?
Even if I remove the println statements, the code (offset 31 to offset 36) still get generated, only at an earlier location, since the println call has been removed.
// Java code
void testMonitor() {
Boolean x = new Boolean(false);
synchronized(x) {
System.out.println("inside synchronized");
System.out.println("blah");
};
System.out.println("done");
}
// JVM bytecode
Offset Instruction Comments (Method: testMonitor)
0 new 42 (java.lang.Boolean)
3 dup
4 iconst_0
5 invokespecial 44 (java.lang.Boolean.<init>)
8 astore_1 (java.lang.Boolean x)
9 aload_1 (java.lang.Boolean x)
10 dup
11 astore_2
12 monitorenter
13 getstatic 15 (java.lang.System.out)
16 ldc 47 (inside synchronized)
18 invokevirtual 23 (java.io.PrintStream.println)
21 getstatic 15 (java.lang.System.out)
24 ldc 49 (blah)
26 invokevirtual 23 (java.io.PrintStream.println)
29 aload_2
30 monitorexit
31 goto 37
34 aload_2
35 monitorexit
36 athrow
37 getstatic 15 (java.lang.System.out)
40 ldc 51 (done)
42 invokevirtual 23 (java.io.PrintStream.println)
45 return
Other threads will be able to continue synchronizing, and calling wait and notify. If the thread with the exception is holding some critical program logic resource, you may need to use try-finally to ensure it is released.
Only one thread can execute inside a synchronized instance method.
Only one thread is allowed to access only one method at any given point of time using a synchronized block. This is a very expensive operation. Locks avoid this by allowing the configuration of various locks for different purpose.
The compiler adds an invisible try/catch block here, to ensure the monitor state gets released (which is documented in the VM specs, see bottom of post). You can verify this by using javap -v
and look at the exception table:
void testMonitor();
Code:
Stack=3, Locals=3, Args_size=1
0: new #15; //class java/lang/Boolean
3: dup
4: iconst_0
5: invokespecial #17; //Method java/lang/Boolean."<init>":(Z)V
8: astore_1
9: aload_1
10: dup
11: astore_2
12: monitorenter
13: getstatic #20; //Field java/lang/System.out:Ljava/io/PrintStream;
16: ldc #26; //String inside synchronized
18: invokevirtual #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
21: getstatic #20; //Field java/lang/System.out:Ljava/io/PrintStream;
24: ldc #34; //String blah
26: invokevirtual #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
29: aload_2
30: monitorexit
31: goto 37
34: aload_2
35: monitorexit
36: athrow
37: getstatic #20; //Field java/lang/System.out:Ljava/io/PrintStream;
40: ldc #36; //String done
42: invokevirtual #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: return
Exception table:
from to target type
13 31 34 any
34 36 34 any
Edit: From the JVM specs:
Normally, a compiler for the Java programming language ensures that the lock operation implemented by a monitorenter instruction executed prior to the execution of the body of the synchronized statement is matched by an unlock operation implemented by a monitorexit instruction whenever the synchronized statement completes, whether completion is normal or abrupt.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With