从源码角度分析java 的 sleep()和wait()究竟有什么区别?

来源:互联网 发布:vmware mac os x 优化 编辑:程序博客网 时间:2024/05/16 09:41


如果不能从根本上理解sleep() 和wait() 之间的区别,再好的解释也会只是当时记得,而后就忘记。本文刨根问题,从源码角度去理解这两个函数的区别。

1. sleep() 的根本实现

jvm sleep()---->jni sleep()-------->  经过层层调用,最终会进入系统的sys_pasue()调用,而sys_pause() 则会调用 schedule(), schedule() 则会主动放弃cpu 时间片,进入等待队列

asmlinkage int sys_pause(void){current->state = TASK_INTERRUPTIBLE;schedule();return -ERESTARTNOHAND;}


2. wait() 的根本实现

先查看jdk\src\share\native\java\lang\Object.c

#include <stdio.h>#include <signal.h>#include <limits.h>#include "jni.h"#include "jni_util.h"#include "jvm.h"#include "java_lang_Object.h"static JNINativeMethod methods[] = {    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},};

可以看到wait 对应的是 native  方法是JVM_MonitorWait, 继续查看JVM_MonitorWait的实现
JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))  JVMWrapper("JVM_MonitorWait");  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));  //调用 JNIHandles::resolve_non_null 函数将 jobject 类型的 handle 转化为 oop  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


接着分析ObjectSynchronizer::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);  /* This dummy call is in place to get around dtrace bug 6254741.  Once     that's fixed we can uncomment the following line and remove the call */  // DTRACE_MONITOR_PROBE(waited, monitor, obj(), THREAD);  dtrace_waited_probe(monitor, obj, THREAD);}

将调用ObjectMonitor 的wait 方法
void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {     ......   // create a node to be put into the queue   // Critically, after we reset() the event but prior to park(), we must check   // for a pending interrupt.   ObjectWaiter node(Self);   node.TState = ObjectWaiter::TS_WAIT ;   Self->_ParkEvent->reset() ;   OrderAccess::fence();          // ST into Event; membar ; LD interrupted-flag   // Enter the waiting queue, which is a circular doubly linked list in this case   // but it could be a priority queue or any data structure.   // _WaitSetLock protects the wait queue.  Normally the wait queue is accessed only   // by the the owner of the monitor *except* in the case where park()   // returns because of a timeout of interrupt.  Contention is exceptionally rare   // so we use a simple spin-lock instead of a heavier-weight blocking lock.   Thread::SpinAcquire (&_WaitSetLock, "WaitSet - add") ;   AddWaiter (&node) ;   Thread::SpinRelease (&_WaitSetLock) ;   ....   int ret = OS_OK ;   int WasNotified = 0 ;   { // State transition wrappers     OSThread* osthread = Self->osthread();     OSThreadWaitState osts(osthread, true);     {       ThreadBlockInVM tbivm(jt);       // Thread is in thread_blocked state and oop access is unsafe.       jt->set_suspend_equivalent();       if (interruptible && (Thread::is_interrupted(THREAD, false) || HAS_PENDING_EXCEPTION)) {           // Intentionally empty       } else       if (node._notified == 0) {         if (millis <= 0) {            Self->_ParkEvent->park () ;         } else {            ret = Self->_ParkEvent->park (millis) ;         }       }       // were we externally suspended while we were waiting?       if (ExitSuspendEquivalent (jt)) {          // TODO-FIXME: add -- if succ == Self then succ = null.          jt->java_suspend_self();       }     } // Exit thread safepoint: transition _thread_blocked -> _thread_in_vm....}

这段函数相当的长,为了从宏观上理解流程,我们删去了很多中间部分,重点在  ObjectWaiter node(Self);   Self 是Thread 对象,将Thread 和 ObjectWaiter 关联起来,
ObjectMonitor::AddWaiter() 将 node加入到  ObjectWaiter 的_WaitSet 中


Self->_ParkEvent->park () ;

将会调用

void os::PlatformEvent::park() {       // AKA "down()"  // Invariant: Only the thread associated with the Event/PlatformEvent  // may call park().  // TODO: assert that _Assoc != NULL or _Assoc == Self  int v ;  for (;;) {      v = _Event ;      if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ;  }  guarantee (v >= 0, "invariant") ;  if (v == 0) {     // Do this the hard way by blocking ...     int status = pthread_mutex_lock(_mutex);     assert_status(status == 0, status, "mutex_lock");     guarantee (_nParked == 0, "invariant") ;     ++ _nParked ;     while (_Event < 0) {        status = pthread_cond_wait(_cond, _mutex);        // for some reason, under 2.7 lwp_cond_wait() may return ETIME ...        // Treat this the same as if the wait was interrupted        if (status == ETIME) { status = EINTR; }        assert_status(status == 0 || status == EINTR, status, "cond_wait");     }     -- _nParked ;    // In theory we could move the ST of 0 into _Event past the unlock(),    // but then we'd need a MEMBAR after the ST.    _Event = 0 ;     status = pthread_mutex_unlock(_mutex);     assert_status(status == 0, status, "mutex_unlock");  }  guarantee (_Event >= 0, "invariant") ;}
从代码中可以看出线程将进入等待中

本质结论:
1.sleep() 只是主动调用schedule() ,放弃CPU时间片
2.wait() 线程并没有主动放弃CPU, 在linux 下 调用 pthread_cond_wait() 等待条件满足再次进入Runnning 状态


本文从宏观上分析了sleep() 和wait()的 区别,中间还有很多的细节,值得去学习,后续将继续深化。


0 0