美文网首页
阻塞线程的相关方法分析(包括HotSpot层源码)

阻塞线程的相关方法分析(包括HotSpot层源码)

作者: 王侦 | 来源:发表于2019-07-27 08:31 被阅读0次

    1.Object的wait/notifyAll

    1.1 官方文档

        public final void wait() throws InterruptedException {
            wait(0);
        }
    
    Causes the current thread to wait until another 
    thread invokes the notify() method or the notifyAll() 
    method for this object. In other words, this method 
    behaves exactly as if it simply performs the call 
    wait(0).
    
    The current thread must own this object's monitor. 
    The thread releases ownership of this monitor and 
    waits until another thread notifies threads waiting on 
    this object's monitor to wake up either through a call 
    to the notify method or the notifyAll method. The 
    thread then waits until it can re-obtain ownership of 
    the monitor and resumes execution.
    
    As in the one argument version, interrupts and 
    spurious wakeups are possible, and this method 
    should always be used in a loop:
    

    导致当前线程等待,直到另一个线程以此对象调用notify()方法或notifyAll()方法。 换句话说,此方法的行为就像它只是执行调用wait(0)一样。

    当前线程必须拥有此对象的monitor。 线程释放此monitor的所有权并等待,直到另一个线程通过调用notify方法或notifyAll方法通知线程等待此对象monitor以便唤醒。 线程等待直到它可以重新获得monitor的所有权并继续执行。

    与一个参数版本中一样,可能会发生中断和虚假唤醒的,所以此方法应始终在循环中使用:

         synchronized (obj) {
             while (<condition does not hold>)
                 obj.wait();
             ... // Perform action appropriate to condition
         }
    

    此方法只能被对象monitor持有者调用。

        public final void wait(long timeout, int nanos) throws InterruptedException {
            if (timeout < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
    
            if (nanos < 0 || nanos > 999999) {
                throw new IllegalArgumentException(
                                    "nanosecond timeout value out of range");
            }
    
            if (nanos > 0) {
                timeout++;
            }
    
            wait(timeout);
        }
    
    public final native void wait(long timeout) throws InterruptedException;
    
    Causes the current thread to wait until either another thread invokes 
    the notify() method or the notifyAll() method for this object, or a 
    specified amount of time has elapsed.
    
    The current thread must own this object's monitor.
    
    This method causes the current thread (call it T) to place itself in the 
    wait set for this object and then to relinquish any and all 
    synchronization claims on this object. Thread T becomes disabled for 
    thread scheduling purposes and lies dormant until one of four things 
    happens:
    * Some other thread invokes the notify method for this object and 
      thread T happens to be arbitrarily chosen as the thread to be 
      awakened.
    * Some other thread invokes the notifyAll method for this object.
    * Some other thread interrupts thread T.
    * The specified amount of real time has elapsed, more or less. If 
      timeout is zero, however, then real time is not taken into consideration 
      and the thread simply waits until notified.
    
    The thread T is then removed from the wait set for this object and re-
    enabled for thread scheduling. It then competes in the usual manner 
    with other threads for the right to synchronize on the object; once it 
    has gained control of the object, all its synchronization claims on the 
    object are restored to the status quo ante - that is, to the situation as 
    of the time that the wait method was invoked. Thread T then returns 
    from the invocation of the wait method. Thus, on return from the wait 
    method, the synchronization state of the object and of thread T is 
    exactly as it was when the wait method was invoked.
    
    A thread can also wake up without being notified, interrupted, or 
    timing out, a so-called spurious wakeup. While this will rarely occur in 
    practice, applications must guard against it by testing for the condition 
    that should have caused the thread to be awakened, and continuing 
    to wait if the condition is not satisfied. In other words, waits should 
    always occur in loops, like this one:
    

    导致当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法,或者已经超时。

    当前线程必须拥有此对象的monitor器。

    此方法使当前线程(称为T)将自身置于此对象的等待集中,然后放弃此对象上的任何和所有同步声明。线程T因线程调度而被禁用,并且在发生以下四种情况之一之前处于休眠状态:

    • 其他线程调用此对象的notify方法,并且线程T恰好被任意选为要被唤醒的线程。
    • 其他线程为此对象调用notifyAll方法。
    • 其他线程中断线程T.
    • 定时已到。但是如果定时为0,则会一直等到直到被通知。

    从该对象的等待集中删除线程T,线程T重新进入线程调度。然后它以通常的方式与其他线程竞争,以便在对象上进行同步;一旦它获得了对象的控制权,它在对象上的所有同步声明都将恢复到原状 - 即之前调用wait方法时的情况。线程T然后从wait方法的调用返回。因此,从wait方法返回时,对象和线程T的同步状态与调用wait方法时的状态完全相同。

    线程也可以在没有被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防范它,并且如果条件不满足则继续等待。换句话说,等待应该总是出现在循环中,如下所示:

         synchronized (obj) {
             while (<condition does not hold>)
                 obj.wait(timeout);
             ... // Perform action appropriate to condition
         }
    
    (For more information on this topic, see Section 3.2.3 in Doug Lea's 
    "Concurrent Programming in Java (Second Edition)" (Addison-
    Wesley, 2000), or Item 50 in Joshua Bloch's "Effective Java 
    Programming Language Guide" (Addison-Wesley, 2001).
    
    If the current thread is interrupted by any thread before or while it is 
    waiting, then an InterruptedException is thrown. This exception is not 
    thrown until the lock status of this object has been restored as 
    described above.
    
    Note that the wait method, as it places the current thread into the wait 
    set for this object, unlocks only this object; any other objects on which 
    the current thread may be synchronized remain locked while the 
    thread waits.
    
    This method should only be called by a thread that is the owner of this 
    object's monitor. See the notify method for a description of the ways in 
    which a thread can become the owner of a monitor.
    

    (有关该主题的更多信息,请参阅Doug Lea的“Concurrent Programming in Java(第二版)”(Addison-Wesley,2000)中的第3.2.3节,或Joshua Bloch的“Effective Java Programming Language Guide”中的第50项(Addison-Wesley,2001年)。

    如果当前线程在等待之前或期间被任何线程中断,则抛出InterruptedException。 在如上所述恢复此对象的锁定状态之前,不会抛出此异常。

    请注意,wait方法将当前线程放入此对象的等待集中,仅解锁此对象; 在线程等待时线程可能在任何其他对象上同步,因此会保持锁定状态。

    此方法只能被持有对象monitor的线程调用。

        public final native void notify();
    
    Wakes up a single thread that is waiting on this object's monitor. If any 
    threads are waiting on this object, one of them is chosen to be 
    awakened. The choice is arbitrary and occurs at the discretion of the 
    implementation. A thread waits on an object's monitor by calling one 
    of the wait methods.
    
    The awakened thread will not be able to proceed until the current 
    thread relinquishes the lock on this object. The awakened thread will 
    compete in the usual manner with any other threads that might be 
    actively competing to synchronize on this object; for example, the 
    awakened thread enjoys no reliable privilege or disadvantage in being
     the next thread to lock this object.
    
    This method should only be called by a thread that is the owner of this 
    object's monitor. A thread becomes the owner of the object's monitor 
    in one of three ways:
    * By executing a synchronized instance method of that object.
    * By executing the body of a synchronized statement that 
      synchronizes on the object.
    * For objects of type Class, by executing a synchronized static 
      method of that class.
    
    Only one thread at a time can own an object's monitor.
    

    唤醒正在此对象monitor上等待的单个线程。 如果有任何线程正在等待此对象,则选择其中一个线程被唤醒。 选择是任意的,由具体实现决定。 线程通过调用对象的wait方法等待对象的monitor。

    唤醒的线程将无法继续直到当前线程放弃对此对象的锁定。 唤醒的线程将以通常的方式与可能主动竞争同步此对象的任何其他线程竞争; 例如,唤醒线程在成为下一个锁定此对象的线程中没有可靠的特权或劣势。

    此方法只应由持有对象monitor的线程调用。 线程以三种方式之一成为对象monitor的所有者:

    • 通过执行该对象的同步实例方法。
    • 通过执行在对象上同步的synchronized语句的主体。
    • 对于Class类型的对象,通过执行该类的同步静态方法。

    在任一时刻只有一个线程持有对象的monitor。

        public final native void notifyAll();
    

    1.2 底层对应的native方法

    wz@wz-All-Series:~/openjdk/jdk8u-src$ grep -rn  "notify" |grep JVM
    jdk/src/share/native/java/lang/Object.c:45:    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    jdk/src/share/native/java/lang/Object.c:46:    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    
    

    查看jdk/src/share/native/java/lang/Object.c文件:

    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},
    };
    
    JNIEXPORT void JNICALL
    Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
    {
        (*env)->RegisterNatives(env, cls,
         
    
    wz@wz-All-Series:~/openjdk/jdk8u-src$ grep -rn "JVM_MonitorWait" .
    ./hotspot/src/share/vm/prims/jvm.cpp:555:JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
    

    对应的三个方法如下:

    1.2.1 JVM_MonitorWait

    JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
      JVMWrapper("JVM_MonitorWait");
      Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
      JavaThreadInObjectWaitState jtiows(thread, ms != 0);
      if (JvmtiExport::should_post_monitor_wait()) {
        JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);
    
        // The current thread already owns the monitor and it has not yet
        // been added to the wait queue so the current thread cannot be
        // made the successor. This means that the JVMTI_EVENT_MONITOR_WAIT
        // event handler cannot accidentally consume an unpark() meant for
        // the ParkEvent associated with this ObjectMonitor.
      }
      ObjectSynchronizer::wait(obj, ms, CHECK);
    JVM_END
    
    wz@wz-All-Series:~/openjdk/jdk8u-src$ grep -rn "class ObjectSynchronizer" .
    ./hotspot/src/share/vm/runtime/synchronizer.hpp:37:class ObjectSynchronizer : AllStatic {
    
    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:

    class ObjectMonitor {
     public:
      enum {
        OM_OK,                    // no error
        OM_SYSTEM_ERROR,          // operating system error
        OM_ILLEGAL_MONITOR_STATE, // IllegalMonitorStateException
        OM_INTERRUPTED,           // Thread.interrupt()
        OM_TIMED_OUT              // Object.wait() timed out
      };
    
      bool      try_enter (TRAPS) ;
      void      enter(TRAPS);
      void      exit(bool not_suspended, TRAPS);
      void      wait(jlong millis, bool interruptable, TRAPS);
      void      notify(TRAPS);
      void      notifyAll(TRAPS);
    
    void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
       Thread * const Self = THREAD ;
       assert(Self->is_Java_thread(), "Must be Java thread!");
       JavaThread *jt = (JavaThread *)THREAD;
    
       DeferredInitialize () ;
    
       // Throw IMSX or IEX.
       CHECK_OWNER();
    
       EventJavaMonitorWait event;
    
       // check for a pending interrupt
       if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) {
         // post monitor waited event.  Note that this is past-tense, we are done waiting.
         if (JvmtiExport::should_post_monitor_waited()) {
            // Note: 'false' parameter is passed here because the
            // wait was not timed out due to thread interrupt.
            JvmtiExport::post_monitor_waited(jt, this, false);
    
            // In this short circuit of the monitor wait protocol, the
            // current thread never drops ownership of the monitor and
            // never gets added to the wait queue so the current thread
            // cannot be made the successor. This means that the
            // JVMTI_EVENT_MONITOR_WAITED event handler cannot accidentally
            // consume an unpark() meant for the ParkEvent associated with
            // this ObjectMonitor.
         }
         if (event.should_commit()) {
           post_monitor_wait_event(&event, 0, millis, false);
         }
         TEVENT (Wait - Throw IEX) ;
         THROW(vmSymbols::java_lang_InterruptedException());
         return ;
       }
    
       TEVENT (Wait) ;
    
       assert (Self->_Stalled == 0, "invariant") ;
       Self->_Stalled = intptr_t(this) ;
       jt->set_current_waiting_monitor(this);
    
       // 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) ;
    
       if ((SyncFlags & 4) == 0) {
          _Responsible = NULL ;
       }
       intptr_t save = _recursions; // record the old recursion count
       _waiters++;                  // increment the number of waiters
       _recursions = 0;             // set the recursion level to be 1
       exit (true, Self) ;                    // exit the monitor
       guarantee (_owner != Self, "invariant") ;
    
       // The thread is on the WaitSet list - now park() it.
       // On MP systems it's conceivable that a brief spin before we park
       // could be profitable.
       //
       // TODO-FIXME: change the following logic to a loop of the form
       //   while (!timeout && !interrupted && _notified == 0) park()
    
       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
    
    
         // Node may be on the WaitSet, the EntryList (or cxq), or in transition
         // from the WaitSet to the EntryList.
         // See if we need to remove Node from the WaitSet.
         // We use double-checked locking to avoid grabbing _WaitSetLock
         // if the thread is not on the wait queue.
         //
         // Note that we don't need a fence before the fetch of TState.
         // In the worst case we'll fetch a old-stale value of TS_WAIT previously
         // written by the is thread. (perhaps the fetch might even be satisfied
         // by a look-aside into the processor's own store buffer, although given
         // the length of the code path between the prior ST and this load that's
         // highly unlikely).  If the following LD fetches a stale TS_WAIT value
         // then we'll acquire the lock and then re-fetch a fresh TState value.
         // That is, we fail toward safety.
    
         if (node.TState == ObjectWaiter::TS_WAIT) {
             Thread::SpinAcquire (&_WaitSetLock, "WaitSet - unlink") ;
             if (node.TState == ObjectWaiter::TS_WAIT) {
                DequeueSpecificWaiter (&node) ;       // unlink from WaitSet
                assert(node._notified == 0, "invariant");
                node.TState = ObjectWaiter::TS_RUN ;
             }
             Thread::SpinRelease (&_WaitSetLock) ;
         }
    
         // The thread is now either on off-list (TS_RUN),
         // on the EntryList (TS_ENTER), or on the cxq (TS_CXQ).
         // The Node's TState variable is stable from the perspective of this thread.
         // No other threads will asynchronously modify TState.
         guarantee (node.TState != ObjectWaiter::TS_WAIT, "invariant") ;
         OrderAccess::loadload() ;
         if (_succ == Self) _succ = NULL ;
         WasNotified = node._notified ;
    
         // Reentry phase -- reacquire the monitor.
         // re-enter contended monitor after object.wait().
         // retain OBJECT_WAIT state until re-enter successfully completes
         // Thread state is thread_in_vm and oop access is again safe,
         // although the raw address of the object may have changed.
         // (Don't cache naked oops over safepoints, of course).
    
         // post monitor waited event. Note that this is past-tense, we are done waiting.
         if (JvmtiExport::should_post_monitor_waited()) {
           JvmtiExport::post_monitor_waited(jt, this, ret == OS_TIMEOUT);
    
           if (node._notified != 0 && _succ == Self) {
             // In this part of the monitor wait-notify-reenter protocol it
             // is possible (and normal) for another thread to do a fastpath
             // monitor enter-exit while this thread is still trying to get
             // to the reenter portion of the protocol.
             //
             // The ObjectMonitor was notified and the current thread is
             // the successor which also means that an unpark() has already
             // been done. The JVMTI_EVENT_MONITOR_WAITED event handler can
             // consume the unpark() that was done when the successor was
             // set because the same ParkEvent is shared between Java
             // monitors and JVM/TI RawMonitors (for now).
             //
             // We redo the unpark() to ensure forward progress, i.e., we
             // don't want all pending threads hanging (parked) with none
             // entering the unlocked monitor.
             node._event->unpark();
           }
         }
    
         if (event.should_commit()) {
           post_monitor_wait_event(&event, node._notifier_tid, millis, ret == OS_TIMEOUT);
         }
    
         OrderAccess::fence() ;
    
         assert (Self->_Stalled != 0, "invariant") ;
         Self->_Stalled = 0 ;
    
         assert (_owner != Self, "invariant") ;
         ObjectWaiter::TStates v = node.TState ;
         if (v == ObjectWaiter::TS_RUN) {
             enter (Self) ;
         } else {
             guarantee (v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant") ;
             ReenterI (Self, &node) ;
             node.wait_reenter_end(this);
         }
    
         // Self has reacquired the lock.
         // Lifecycle - the node representing Self must not appear on any queues.
         // Node is about to go out-of-scope, but even if it were immortal we wouldn't
         // want residual elements associated with this thread left on any lists.
         guarantee (node.TState == ObjectWaiter::TS_RUN, "invariant") ;
         assert    (_owner == Self, "invariant") ;
         assert    (_succ != Self , "invariant") ;
       } // OSThreadWaitState()
    
       jt->set_current_waiting_monitor(NULL);
    
       guarantee (_recursions == 0, "invariant") ;
       _recursions = save;     // restore the old recursion count
       _waiters--;             // decrement the number of waiters
    
       // Verify a few postconditions
       assert (_owner == Self       , "invariant") ;
       assert (_succ  != Self       , "invariant") ;
       assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
    
       if (SyncFlags & 32) {
          OrderAccess::fence() ;
       }
    
       // check if the notification happened
       if (!WasNotified) {
         // no, it could be timeout or Thread.interrupt() or both
         // check for interrupt event, otherwise it is timeout
         if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) {
           TEVENT (Wait - throw IEX from epilog) ;
           THROW(vmSymbols::java_lang_InterruptedException());
         }
       }
    
       // NOTE: Spurious wake up will be consider as timeout.
       // Monitor notify has precedence over thread interrupt.
    }
    
    

    1.2.2 JVM_MonitorNotify

    JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))
      JVMWrapper("JVM_MonitorNotify");
      Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
      ObjectSynchronizer::notify(obj, CHECK);
    JVM_END
    

    1.2.3 JVM_MonitorNotifyAll

    JVM_ENTRY(void, JVM_MonitorNotifyAll(JNIEnv* env, jobject handle))
      JVMWrapper("JVM_MonitorNotifyAll");
      Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
      ObjectSynchronizer::notifyall(obj, CHECK);
    JVM_END
    
    void ObjectMonitor::notifyAll(TRAPS) {
      CHECK_OWNER();
      ObjectWaiter* iterator;
      if (_WaitSet == NULL) {
          TEVENT (Empty-NotifyAll) ;
          return ;
      }
      DTRACE_MONITOR_PROBE(notifyAll, this, object(), THREAD);
    
      int Policy = Knob_MoveNotifyee ;
      int Tally = 0 ;
      Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notifyall") ;
    
      for (;;) {
         iterator = DequeueWaiter () ;
         if (iterator == NULL) break ;
         TEVENT (NotifyAll - Transfer1) ;
         ++Tally ;
    
         // Disposition - what might we do with iterator ?
         // a.  add it directly to the EntryList - either tail or head.
         // b.  push it onto the front of the _cxq.
         // For now we use (a).
    
         guarantee (iterator->TState == ObjectWaiter::TS_WAIT, "invariant") ;
         guarantee (iterator->_notified == 0, "invariant") ;
         iterator->_notified = 1 ;
         Thread * Self = THREAD;
         iterator->_notifier_tid = Self->osthread()->thread_id();
         if (Policy != 4) {
            iterator->TState = ObjectWaiter::TS_ENTER ;
         }
    
         ObjectWaiter * List = _EntryList ;
         if (List != NULL) {
            assert (List->_prev == NULL, "invariant") ;
            assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;
            assert (List != iterator, "invariant") ;
         }
    
         if (Policy == 0) {       // prepend to EntryList
             if (List == NULL) {
                 iterator->_next = iterator->_prev = NULL ;
                 _EntryList = iterator ;
             } else {
                 List->_prev = iterator ;
                 iterator->_next = List ;
                 iterator->_prev = NULL ;
                 _EntryList = iterator ;
            }
         } else
         if (Policy == 1) {      // append to EntryList
             if (List == NULL) {
                 iterator->_next = iterator->_prev = NULL ;
                 _EntryList = iterator ;
             } else {
                // CONSIDER:  finding the tail currently requires a linear-time walk of
                // the EntryList.  We can make tail access constant-time by converting to
                // a CDLL instead of using our current DLL.
                ObjectWaiter * Tail ;
                for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ;
                assert (Tail != NULL && Tail->_next == NULL, "invariant") ;
                Tail->_next = iterator ;
                iterator->_prev = Tail ;
                iterator->_next = NULL ;
            }
         } else
         if (Policy == 2) {      // prepend to cxq
             // prepend to cxq
             iterator->TState = ObjectWaiter::TS_CXQ ;
             for (;;) {
                 ObjectWaiter * Front = _cxq ;
                 iterator->_next = Front ;
                 if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {
                     break ;
                 }
             }
         } else
         if (Policy == 3) {      // append to cxq
            iterator->TState = ObjectWaiter::TS_CXQ ;
            for (;;) {
                ObjectWaiter * Tail ;
                Tail = _cxq ;
                if (Tail == NULL) {
                    iterator->_next = NULL ;
                    if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) {
                       break ;
                    }
                } else {
                    while (Tail->_next != NULL) Tail = Tail->_next ;
                    Tail->_next = iterator ;
                    iterator->_prev = Tail ;
                    iterator->_next = NULL ;
                    break ;
                }
            }
         } else {
            ParkEvent * ev = iterator->_event ;
            iterator->TState = ObjectWaiter::TS_RUN ;
            OrderAccess::fence() ;
            ev->unpark() ;
         }
    
         if (Policy < 4) {
           iterator->wait_reenter_begin(this);
         }
    
         // _WaitSetLock protects the wait queue, not the EntryList.  We could
         // move the add-to-EntryList operation, above, outside the critical section
         // protected by _WaitSetLock.  In practice that's not useful.  With the
         // exception of  wait() timeouts and interrupts the monitor owner
         // is the only thread that grabs _WaitSetLock.  There's almost no contention
         // on _WaitSetLock so it's not profitable to reduce the length of the
         // critical section.
      }
    
      Thread::SpinRelease (&_WaitSetLock) ;
    
      if (Tally != 0 && ObjectMonitor::_sync_Notifications != NULL) {
         ObjectMonitor::_sync_Notifications->inc(Tally) ;
      }
    }
    

    2.Thread的join

        public final void join() throws InterruptedException {
            join(0);
        }
    

    等待该线程终止。

        public final synchronized void join(long millis)
        throws InterruptedException {
            long base = System.currentTimeMillis();
            long now = 0;
    
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
    
            if (millis == 0) {
                while (isAlive()) {
                    wait(0);
                }
            } else {
                while (isAlive()) {
                    long delay = millis - now;
                    if (delay <= 0) {
                        break;
                    }
                    wait(delay);
                    now = System.currentTimeMillis() - base;
                }
            }
        }
    

    最多等待该线程millis毫秒,0表示无限期等待。

    此实现在this.isAlive时循环调用this.wait。 当一个线程终止时,将调用this.notifyAll方法。 建议应用程序不要在Thread实例上使用wait,notify或notifyAll。

        public final synchronized void join(long millis, int nanos)
        throws InterruptedException {
    
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
    
            if (nanos < 0 || nanos > 999999) {
                throw new IllegalArgumentException(
                                    "nanosecond timeout value out of range");
            }
    
            if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
                millis++;
            }
    
            join(millis);
        }
    

    3.Thread的sleep

    public static native void sleep(long millis) throws InterruptedException;
    
    Causes the currently executing thread to sleep (temporarily cease 
    execution) for the specified number of milliseconds, subject to the 
    precision and accuracy of system timers and schedulers. The thread 
    does not lose ownership of any monitors.
    

    导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。 该线程不会失去任何monitor的所有权。

        public static void sleep(long millis, int nanos)
        throws InterruptedException {
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
    
            if (nanos < 0 || nanos > 999999) {
                throw new IllegalArgumentException(
                                    "nanosecond timeout value out of range");
            }
    
            if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
                millis++;
            }
    
            sleep(millis);
        }
    

    ./hotspot/src/share/vm/prims/jvm.cpp:

    JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
      JVMWrapper("JVM_Sleep");
    
      if (millis < 0) {
        THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
      }
    
      if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {
        THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
      }
    
      // Save current thread state and restore it at the end of this block.
      // And set new thread state to SLEEPING.
      JavaThreadSleepState jtss(thread);
    
    #ifndef USDT2
      HS_DTRACE_PROBE1(hotspot, thread__sleep__begin, millis);
    #else /* USDT2 */
      HOTSPOT_THREAD_SLEEP_BEGIN(
                                 millis);
    #endif /* USDT2 */
    
      EventThreadSleep event;
    
      if (millis == 0) {
        // When ConvertSleepToYield is on, this matches the classic VM implementation of
        // JVM_Sleep. Critical for similar threading behaviour (Win32)
        // It appears that in certain GUI contexts, it may be beneficial to do a short sleep
        // for SOLARIS
        if (ConvertSleepToYield) {
          os::yield();
        } else {
          ThreadState old_state = thread->osthread()->get_state();
          thread->osthread()->set_state(SLEEPING);
          os::sleep(thread, MinSleepInterval, false);
          thread->osthread()->set_state(old_state);
        }
      } else {
        ThreadState old_state = thread->osthread()->get_state();
        thread->osthread()->set_state(SLEEPING);
        if (os::sleep(thread, millis, true) == OS_INTRPT) {
          // An asynchronous exception (e.g., ThreadDeathException) could have been thrown on
          // us while we were sleeping. We do not overwrite those.
          if (!HAS_PENDING_EXCEPTION) {
            if (event.should_commit()) {
              event.set_time(millis);
              event.commit();
            }
    #ifndef USDT2
            HS_DTRACE_PROBE1(hotspot, thread__sleep__end,1);
    #else /* USDT2 */
            HOTSPOT_THREAD_SLEEP_END(
                                     1);
    #endif /* USDT2 */
            // TODO-FIXME: THROW_MSG returns which means we will not call set_state()
            // to properly restore the thread state.  That's likely wrong.
            THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
          }
        }
        thread->osthread()->set_state(old_state);
      }
      if (event.should_commit()) {
        event.set_time(millis);
        event.commit();
      }
    #ifndef USDT2
      HS_DTRACE_PROBE1(hotspot, thread__sleep__end,0);
    #else /* USDT2 */
      HOTSPOT_THREAD_SLEEP_END(
                               0);
    #endif /* USDT2 */
    JVM_END
    

    继续查看os::sleep,在/openjdk/jdk8u-src/hotspot/src/os/linux/vm/os_linux.cpp中:

    int os::sleep(Thread* thread, jlong millis, bool interruptible) {
      assert(thread == Thread::current(),  "thread consistency check");
    
      ParkEvent * const slp = thread->_SleepEvent ;
      slp->reset() ;
      OrderAccess::fence() ;
    
      if (interruptible) {
        jlong prevtime = javaTimeNanos();
    
        for (;;) {
          if (os::is_interrupted(thread, true)) {
            return OS_INTRPT;
          }
    
          jlong newtime = javaTimeNanos();
    
          if (newtime - prevtime < 0) {
            // time moving backwards, should only happen if no monotonic clock
            // not a guarantee() because JVM should not abort on kernel/glibc bugs
            assert(!Linux::supports_monotonic_clock(), "time moving backwards");
          } else {
            millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
          }
    
          if(millis <= 0) {
            return OS_OK;
          }
    
          prevtime = newtime;
    
          {
            assert(thread->is_Java_thread(), "sanity check");
            JavaThread *jt = (JavaThread *) thread;
            ThreadBlockInVM tbivm(jt);
            OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */);
    
            jt->set_suspend_equivalent();
            // cleared by handle_special_suspend_equivalent_condition() or
            // java_suspend_self() via check_and_wait_while_suspended()
    
            slp->park(millis);
    
            // were we externally suspended while we were waiting?
            jt->check_and_wait_while_suspended();
          }
        }
      } else {
        OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
        jlong prevtime = javaTimeNanos();
    
        for (;;) {
          // It'd be nice to avoid the back-to-back javaTimeNanos() calls on
          // the 1st iteration ...
          jlong newtime = javaTimeNanos();
    
          if (newtime - prevtime < 0) {
            // time moving backwards, should only happen if no monotonic clock
            // not a guarantee() because JVM should not abort on kernel/glibc bugs
            assert(!Linux::supports_monotonic_clock(), "time moving backwards");
          } else {
            millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
          }
    
          if(millis <= 0) break ;
    
          prevtime = newtime;
          slp->park(millis);
        }
        return OS_OK ;
      }
    }
    

    4.LockSupport.park/unpark

    参考LockSupport及HotSpot层Parker::park/unpark分析

        public static void park(Object blocker) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            UNSAFE.park(false, 0L);
            setBlocker(t, null);
        }
    
    
        public static void park() {
            UNSAFE.park(false, 0L);
        }
    
        public static void unpark(Thread thread) {
            if (thread != null)
                UNSAFE.unpark(thread);
        }
    

    相关文章

      网友评论

          本文标题:阻塞线程的相关方法分析(包括HotSpot层源码)

          本文链接:https://www.haomeiwen.com/subject/baghrctx.html