细节背后:为什么线程协作之前必须先获得锁?
2009年10月3日
没有评论
为什么Object.wait()/notify()/notifyAll() 之前必须获得锁? 这是JLS的规定。Wait-notify机制是围绕监控器锁进行的,获得锁是很自然的前提,自身没有拿到锁之前,怎么能够尝试去操作靠锁来调控的线程呢?不过今天偶尔有时间,就看下Sun Hotspot是怎么实现这一机制的。
当我们执行下面的代码时,线程会抛出异常java.lang.IllegalMonitorStateException: current thread not owner。
public class WaitNotifyCompilerCode { private String aString = "Hello World!"; public static void main(String[] args) { System.out.println("Execute start ...."); final WaitNotifyCompilerCode w = new WaitNotifyCompilerCode(); w.wait1SecAndPrintString(); System.out.println("Execute end ...."); } public void 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(Native Method) 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)
最近评论