Updated: 10/9/96 PatrickB
Return to the Java Hall of Shame
public void foo(Object o) {
synchronized(this) {
o.toString();
}
}
results in the following byte-code:
Method void foo(java.lang.Object)
0 aload_0
1 astore_2
2 aload_2
3 monitorenter
4 aload_1
5 invokevirtual #3 <Method java.lang.Object.toString()Ljava/lang/String;>
8 pop
9 aload_2
10 monitorexit
11 return
12 aload_2
13 monitorexit
14 athrow
Exception table:
from to target type
4 9 12 any
What does the method do when passed a null object? The method will
grab the monitor lock and then throw a null pointer exception at line
5. Since 5 is in the exception range of 4 to 9 the program will
continue executing at the exception handler at line 12. Consider what
happens when an asynchronous exception is thrown (by the
java.lang.Thread.stop() method) before or immediately after
executing the aload_2 instruction at line 12. The VM checks
if line 12 is in the exception range - it is not. The VM then
considers this exception to be handled at a higher level and the
handler at a higher level is invoked. The monitor lock was never
released.
According to the VM spec it is legal (but not mandatory) to defer handling of asynchronous exceptions until a change in control flow. Any implementation that does this will not have this race condition since the asynchronous exception raised at line 12 will not be detected until the athrow after the monitor is exited. The race condition can be eliminated by having a second exception range that points back to the same exception handler: from line 12 to 13 pointing back to line 12, in this case.
Return to the Java Hall of Shame