Thread中斷方法分析(包括HotSpot源碼)

1.Thread.interrupt

1.1 官方文檔

    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
Interrupts this thread.

Unless the current thread is interrupting itself, which is always 
permitted, the checkAccess method of this thread is invoked, which 
may cause a SecurityException to be thrown.

If this thread is blocked in an invocation of the wait(), wait(long), or 
wait(long, int) methods of the Object class, or of the join(), join(long), 
join(long, int), sleep(long), or sleep(long, int), methods of this class, 
then its interrupt status will be cleared and it will receive an 
InterruptedException.

If this thread is blocked in an I/O operation upon an 
InterruptibleChannel then the channel will be closed, the thread's 
interrupt status will be set, and the thread will receive a 
ClosedByInterruptException.

If this thread is blocked in a Selector then the thread's interrupt status 
will be set and it will return immediately from the selection operation, 
possibly with a non-zero value, just as if the selector's wakeup 
method were invoked.

If none of the previous conditions hold then this thread's interrupt 
status will be set.

Interrupting a thread that is not alive need not have any effect.

中斷此線程。

除非當前線程正在中斷自身(始終允許),否則將調用此線程的checkAccess方法,這可能導致拋出SecurityException。

如果該線程阻塞在如下方法的調用:Object類的wait()、wait(long)或wait(long,int)方法,或者Thread類的join()、join(long)、join(long, int)方法、sleep(long)或sleep(long, int)方法,則它的中斷狀態將被清除,並將收到InterruptedException。

如果線程阻塞在InterruptibleChannel上的I/O操作,則該通道將被關閉,線程的中斷狀態將被置位,並且線程將收到ClosedByInterruptException。

如果此線程阻塞在Selector中,則線程的中斷狀態將被置位,並且它將立即從選擇操作返回,可能具有非零值,就像調用選擇器的喚醒方法一樣。

如果上面的條件都不成立,則將置位該線程的中斷狀態。

中斷不活動的線程不會產生任何影響。

1.2 interrupt0本地方法

wz@wz-All-Series:~/openjdk/jdk8u-src/jdk/src/share/native$ grep -rn "interrupt0"
java/lang/Thread.c:54:    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},

位置openjdk/jdk8u-src/jdk/src/share/native/java/lang/Thread.c

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
    {"resume0",          "()V",        (void *)&JVM_ResumeThread},
    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
    {"yield",            "()V",        (void *)&JVM_Yield},
    {"sleep",            "(J)V",       (void *)&JVM_Sleep},
    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
    {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
wz@wz-All-Series:~/openjdk/jdk8u-src$ grep -rn "JVM_Interrupt" . 
./hotspot/src/share/vm/prims/jvm.cpp:3347:JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))

// Consider: A better way to implement JVM_Interrupt() is to acquire
// Threads_lock to resolve the jthread into a Thread pointer, fetch
// Thread->platformevent, Thread->native_thr, Thread->parker, etc.,
// drop Threads_lock, and the perform the unpark() and thr_kill() operations
// outside the critical section.  Threads_lock is hot so we want to minimize
// the hold-time.  A cleaner interface would be to decompose interrupt into
// two steps.  The 1st phase, performed under Threads_lock, would return
// a closure that'd be invoked after Threads_lock was dropped.
// This tactic is safe as PlatformEvent and Parkers are type-stable (TSM) and
// admit spurious wakeups.

JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_Interrupt");

  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
  oop java_thread = JNIHandles::resolve_non_null(jthread);
  MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
  // We need to re-resolve the java_thread, since a GC might have happened during the
  // acquire of the lock
  JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
  if (thr != NULL) {
    Thread::interrupt(thr);
  }
JVM_END

定位Thread::interrupt

wz@wz-All-Series:~/openjdk/jdk8u-src/hotspot/src$ grep -rn "Thread::interrupt" .
./share/vm/runtime/thread.cpp:804:void Thread::interrupt(Thread* thread) {

/share/vm/runtime/thread.cpp:

void Thread::interrupt(Thread* thread) {
  trace("interrupt", thread);
  debug_only(check_for_dangling_thread_pointer(thread);)
  os::interrupt(thread);
}

到linux目錄下查找:

wz@wz-All-Series:~/openjdk/jdk8u-src/hotspot/src/os/linux/vm$ grep -rn "os::interrupt" .
./os_linux.cpp:4462:void os::interrupt(Thread* thread) {
void os::interrupt(Thread* thread) {
  assert(Thread::current() == thread || Threads_lock->owned_by_self(),
    "possibility of dangling Thread pointer");

  OSThread* osthread = thread->osthread();

  if (!osthread->interrupted()) {
    osthread->set_interrupted(true);
    // More than one thread can get here with the same value of osthread,
    // resulting in multiple notifications.  We do, however, want the store
    // to interrupted() to be visible to other threads before we execute unpark().
    OrderAccess::fence();
    ParkEvent * const slp = thread->_SleepEvent ;
    if (slp != NULL) slp->unpark() ;
  }

  // For JSR166. Unpark even if interrupt status already was set
  if (thread->is_Java_thread())
    ((JavaThread*)thread)->parker()->unpark();

  ParkEvent * ev = thread->_ParkEvent ;
  if (ev != NULL) ev->unpark() ;

}
  • step1.通過thread->osthread()來獲取系統線程
  • step2.如果未設置中斷標誌,則通過osthread->set_interrupted(true)來設置;調用 OrderAccess::fence()屏障指令,保證在調用unpark之前對其他線程可見。
    調用slp->unpark()中斷Thread.sleep()。
  • step3.((JavaThread*)thread)->parker()->unpark()中斷LockSupport.park()。
  • step4.ev->unpark()中斷Object.wait()的方法。
class OSThread: public CHeapObj<mtThread> {
 private:
  volatile jint _interrupted;     // Thread.isInterrupted state
public:
  volatile bool interrupted() const                 { return _interrupted != 0; }
  void set_interrupted(bool z)                      { _interrupted = z ? 1 : 0; }

1.2.1 ParkEvent

wz@wz-All-Series:~/openjdk/jdk8u-src$ grep -rn "class ParkEvent" .
./hotspot/src/share/vm/runtime/park.hpp:118:class ParkEvent : public os::PlatformEvent {
class ParkEvent : public os::PlatformEvent {
  private:
    ParkEvent * FreeNext ;

    // Current association
    Thread * AssociatedWith ;
    intptr_t RawThreadIdentity ;        // LWPID etc
    volatile int Incarnation ;

    // diagnostic : keep track of last thread to wake this thread.
    // this is useful for construction of dependency graphs.
    void * LastWaker ;

  public:
    // MCS-CLH list linkage and Native Mutex/Monitor
    ParkEvent * volatile ListNext ;
    ParkEvent * volatile ListPrev ;
    volatile intptr_t OnList ;
    volatile int TState ;
    volatile int Notified ;             // for native monitor construct
    volatile int IsWaiting ;            // Enqueued on WaitSet


  private:
    static ParkEvent * volatile FreeList ;
    static volatile int ListLock ;

    // It's prudent to mark the dtor as "private"
    // ensuring that it's not visible outside the package.
    // Unfortunately gcc warns about such usage, so
    // we revert to the less desirable "protected" visibility.
    // The other compilers accept private dtors.

  protected:        // Ensure dtor is never invoked
    ~ParkEvent() { guarantee (0, "invariant") ; }

    ParkEvent() : PlatformEvent() {
       AssociatedWith = NULL ;
       FreeNext       = NULL ;
       ListNext       = NULL ;
       ListPrev       = NULL ;
       OnList         = 0 ;
       TState         = 0 ;
       Notified       = 0 ;
       IsWaiting      = 0 ;
    }

    // We use placement-new to force ParkEvent instances to be
    // aligned on 256-byte address boundaries.  This ensures that the least
    // significant byte of a ParkEvent address is always 0.

    void * operator new (size_t sz) throw();
    void operator delete (void * a) ;

  public:
    static ParkEvent * Allocate (Thread * t) ;
    static void Release (ParkEvent * e) ;
} ;

wz@wz-All-Series:~/openjdk/jdk8u-src$ grep -rn "class PlatformEvent" .
./hotspot/src/os/linux/vm/os_linux.hpp:355:class PlatformEvent : public CHeapObj<mtInternal> {

class PlatformEvent : public CHeapObj<mtInternal> {
  private:
    double CachePad [4] ;   // increase odds that _mutex is sole occupant of cache line
    volatile int _Event ;
    volatile int _nParked ;
    pthread_mutex_t _mutex  [1] ;
    pthread_cond_t  _cond   [1] ;
    double PostPad  [2] ;
    Thread * _Assoc ;

  public:       // TODO-FIXME: make dtor private
    ~PlatformEvent() { guarantee (0, "invariant") ; }

  public:
    PlatformEvent() {
      int status;
      status = pthread_cond_init (_cond, os::Linux::condAttr());
      assert_status(status == 0, status, "cond_init");
      status = pthread_mutex_init (_mutex, NULL);
      assert_status(status == 0, status, "mutex_init");
      _Event   = 0 ;
      _nParked = 0 ;
      _Assoc   = NULL ;
    }

    // Use caution with reset() and fired() -- they may require MEMBARs
    void reset() { _Event = 0 ; }
    int  fired() { return _Event; }
    void park () ;
    void unpark () ;
    int  TryPark () ;
    int  park (jlong millis) ; // relative timed-wait only
    void SetAssociation (Thread * a) { _Assoc = a ; }
} ;
void os::PlatformEvent::unpark() {
  // Transitions for _Event:
  //    0 :=> 1
  //    1 :=> 1
  //   -1 :=> either 0 or 1; must signal target thread
  //          That is, we can safely transition _Event from -1 to either
  //          0 or 1. Forcing 1 is slightly more efficient for back-to-back
  //          unpark() calls.
  // See also: "Semaphores in Plan 9" by Mullender & Cox
  //
  // Note: Forcing a transition from "-1" to "1" on an unpark() means
  // that it will take two back-to-back park() calls for the owning
  // thread to block. This has the benefit of forcing a spurious return
  // from the first park() call after an unpark() call which will help
  // shake out uses of park() and unpark() without condition variables.

  if (Atomic::xchg(1, &_Event) >= 0) return;

  // Wait for the thread associated with the event to vacate
  int status = pthread_mutex_lock(_mutex);
  assert_status(status == 0, status, "mutex_lock");
  int AnyWaiters = _nParked;
  assert(AnyWaiters == 0 || AnyWaiters == 1, "invariant");
  if (AnyWaiters != 0 && WorkAroundNPTLTimedWaitHang) {
    AnyWaiters = 0;
    pthread_cond_signal(_cond);
  }
  status = pthread_mutex_unlock(_mutex);
  assert_status(status == 0, status, "mutex_unlock");
  if (AnyWaiters != 0) {
    status = pthread_cond_signal(_cond);
    assert_status(status == 0, status, "cond_signal");
  }

  // Note that we signal() _after dropping the lock for "immortal" Events.
  // This is safe and avoids a common class of  futile wakeups.  In rare
  // circumstances this can cause a thread to return prematurely from
  // cond_{timed}wait() but the spurious wakeup is benign and the victim will
  // simply re-test the condition and re-park itself.
}
  • step1.這裏將_Event設置爲1
  • step2.後面加鎖_mutex來操作_nParked,如果不爲0,則將其設置爲0,並pthread_cond_signal(_cond)喚醒阻塞的線程

2.Thread.interrupted及Thread.isInterrupted

    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
Tests whether the current thread has been interrupted. The 
interrupted status of the thread is cleared by this method. In other 
words, if this method were to be called twice in succession, the 
second call would return false (unless the current thread were 
interrupted again, after the first call had cleared its interrupted status 
and before the second call had examined it).

A thread interruption ignored because a thread was not alive at the 
time of the interrupt will be reflected by this method returning false.

測試當前線程是否已被中斷。 此方法清除線程的中斷狀態。 換句話說,如果要連續兩次調用此方法,則第二次調用將返回false(除非當前線程在第一次調用清除中斷狀態後且在第二次調用檢查之前再次中斷)。

中斷不活動線程將被忽略,並且此方法返回false。

    public boolean isInterrupted() {
        return isInterrupted(false);
    }

測試此線程是否已被中斷。 線程的中斷狀態不受此方法的影響。

中斷不活動線程將被忽略,並且此方法返回false。

    private native boolean isInterrupted(boolean ClearInterrupted);

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

JVM_QUICK_ENTRY(jboolean, JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clear_interrupted))
  JVMWrapper("JVM_IsInterrupted");

  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
  oop java_thread = JNIHandles::resolve_non_null(jthread);
  MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
  // We need to re-resolve the java_thread, since a GC might have happened during the
  // acquire of the lock
  JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
  if (thr == NULL) {
    return JNI_FALSE;
  } else {
    return (jboolean) Thread::is_interrupted(thr, clear_interrupted != 0);
  }
JVM_END
bool Thread::is_interrupted(Thread* thread, bool clear_interrupted) {
  trace("is_interrupted", thread);
  debug_only(check_for_dangling_thread_pointer(thread);)
  // Note:  If clear_interrupted==false, this simply fetches and
  // returns the value of the field osthread()->interrupted().
  return os::is_interrupted(thread, clear_interrupted);
}
bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
  assert(Thread::current() == thread || Threads_lock->owned_by_self(),
    "possibility of dangling Thread pointer");

  OSThread* osthread = thread->osthread();

  bool interrupted = osthread->interrupted();

  if (interrupted && clear_interrupted) {
    osthread->set_interrupted(false);
    // consider thread->_SleepEvent->reset() ... optional optimization
  }

  return interrupted;
}
  • step1.通過thread->osthread()來獲取系統線程,並獲取中斷標識interrupted = osthread->interrupted()
  • step2.如果線程已經被中斷並且clear_interrupted爲true,則清除中斷標誌位。其他情況只是返回中斷標識位。

這也說明了爲什麼interrupted()爲什麼會清除中斷標誌位,因爲其調用的是currentThread().isInterrupted(true),true表示要清除。

wz@wz-All-Series:~/openjdk/jdk8u-src/hotspot/src$ grep -rn "class OSThread" .
./share/vm/runtime/osThread.hpp:61:class OSThread: public CHeapObj<mtThread> {
class OSThread: public CHeapObj<mtThread> {
 private:
  volatile jint _interrupted;     // Thread.isInterrupted state
public:
  volatile bool interrupted() const                 { return _interrupted != 0; }
  void set_interrupted(bool z)                      { _interrupted = z ? 1 : 0; }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章