为什么Object.wait()/notify()/notifyAll() 之前必须获得锁? 这是JLS的规定。Wait-notify机制是围绕监控器锁进行的,获得锁是很自然的前提,自身没有拿到锁之前,怎么能够尝试去操作靠锁来调控的线程呢?不过今天偶尔有时间,就看下Sun Hotspot是怎么实现这一机制的。
当我们执行下面的代码时,线程会抛出异常java.lang.IllegalMonitorStateException: current thread not owner。
publicclass WaitNotifyCompilerCode {privateString aString ="Hello World!"; publicstaticvoid main(String[] args){System.out.println("Execute start ....");final WaitNotifyCompilerCode w =new WaitNotifyCompilerCode(); w.wait1SecAndPrintString();System.out.println("Execute end ....");} publicvoid wait1SecAndPrintString(){try{this.wait(1000);}catch(InterruptedException e){ e.printStackTrace();}System.out.println(aString);} }
异常栈的信息如下:
Exception in thread "main" java.lang.IllegalMonitorStateException: current thread not owner at java.lang.Object.wait(NativeMethod) at com.feihoo.test.waitnotify.WaitNotifyCompilerCode.wait1SecAndPrintString(WaitNotifyCompilerCode.java:35) at com.feihoo.test.waitnotify.WaitNotifyCompilerCode.main(WaitNotifyCompilerCode.java:27)
深入查看 OpenJDK的源码,找到 Object.wait() 函数本地代码:
JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms)) JVMWrapper("JVM_MonitorWait"); Handle obj(THREAD, JNIHandles::resolve_non_null(handle));assert(obj->is_instance()|| obj->is_array(), "JVM_MonitorWait must apply to an object"); JavaThreadInObjectWaitState jtiows(thread, ms !=0);if(JvmtiExport::should_post_monitor_wait()){ JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);} ObjectSynchronizer::wait(obj, ms, CHECK); JVM_END
重点看倒数第二行,调用的函数如下面的代码:
// NOTE: must use heavy weight monitor to handle wait()void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS){if(UseBiasedLocking){ BiasedLocking::revoke_and_rebias(obj, false, THREAD);assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");}if(millis <0){ TEVENT (wait -throw IAX); THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");} ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj()); DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis); monitor->wait(millis, true, THREAD);
而上面的代码中最后一行的函数里,在做实际操作之前调用了下面的宏:
// A macro is used below because there may already be a pending// exception which should not abort the execution of the routines// which use this (which is why we don't put this into check_slow and// call it with a CHECK argument). #define CHECK_OWNER() \ do { \ if (THREAD != _owner) { \ if (THREAD->is_lock_owned((address) _owner)) { \ _owner = THREAD ; /* Convert from basiclock addr to Thread addr */ \ _recursions = 0; \ OwnerIsThread = 1 ; \ } else { \ TEVENT (Throw IMSX) ; \ THROW(vmSymbols::java_lang_IllegalMonitorStateException()); \ } \ } \ } while (false)