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 anyWhat 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