Hotspot Parker和ParkEvent 源碼解析

   目錄

一、LockSupport

1、park

2、unpark

二、Parker

 1、定義

2、Allocate / Release

3、park

4、unpark

三、ParkEvent 

 1、定義

2、Allocate / Release

3、TryPark / park

4、unpark


    Parker是Unsafe類的park和unpark方法的核心,ParkEvent是Thread的sleep方法,synchronized關鍵字中讓線程休眠的核心,本篇博客就詳細探討這兩個類的實現細節。

一、LockSupport

      LockSupport是實現Java Lock接口的關鍵,裏面定義的方法都是靜態方法,且底層實現都是調用sun.misc.Unsafe的方法,其中大部分都是本地方法,其本地方法實現都在hotspot\src\share\vm\prims\Unsafe.cpp中,並不是在正常的jdk\src\share\native目錄下,其本地方法的註冊通過JVM_RegisterUnsafeMethods方法實現,對應於Unsafe類的靜態registerNatives本地方法,即當Unsafe類加載的時候就會通過上述方法完成Unsafe類中其他本地方法的註冊。

1、park

     park類方法有多個版本,如下:

其用途都是讓某個線程處於阻塞(休眠)狀態,操作系統不會再給該線程分配CPU時間片,其實現如下:

public static void park() {
        UNSAFE.park(false, 0L);
    }

//跟park相比就是parkBlocker不一樣
public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }

private static void setBlocker(Thread t, Object arg) {
        //將arg寫入Thread的parkBlocker屬性,該屬性是包內訪問的
        UNSAFE.putObject(t, parkBlockerOffset, arg);
    }

//跟park相比增加了阻塞的時間限制,單位是納秒
public static void parkNanos(long nanos) {
        if (nanos > 0)
            UNSAFE.park(false, nanos);
    }

public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            UNSAFE.park(false, nanos);
            setBlocker(t, null);
        }
    }

//單位是毫秒
public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
    }

public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }

 UnSafe類的putObject和park方法都是本地方法,其實現如下:


//putObject的實現
UNSAFE_ENTRY(void, Unsafe_SetObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h))
  UnsafeWrapper("Unsafe_SetObject");
  //x_h就是參數Object arg
  oop x = JNIHandles::resolve(x_h);
  //obj實際就是參數Thread t
  oop p = JNIHandles::resolve(obj);
  //index_oop_from_field_offset_long方法獲取保存該屬性的地址,oop_store負責將x寫入該地址
  if (UseCompressedOops) {
    oop_store((narrowOop*)index_oop_from_field_offset_long(p, offset), x);
  } else {
    oop_store((oop*)index_oop_from_field_offset_long(p, offset), x);
  }
UNSAFE_END

UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time))
  UnsafeWrapper("Unsafe_Park");
  EventThreadPark event;
 
  JavaThreadParkedState jtps(thread, time != 0);
  //獲取parker,讓當前線程休眠
  thread->parker()->park(isAbsolute != 0, time);

  if (event.should_commit()) {
    //發佈事件
    oop obj = thread->current_park_blocker();
    event.set_klass((obj != NULL) ? obj->klass() : NULL);
    event.set_timeout(time);
    event.set_address((obj != NULL) ? (TYPE_ADDRESS) cast_from_oop<uintptr_t>(obj) : 0);
    event.commit();
  }
UNSAFE_END

JavaThreadParkedState通過構造方法和析構方法來修改線程的狀態,並記錄鎖等待的次數和耗時,其實現如下:

2、unpark

     unpark方法只有一個版本,注意unpark不一定對當前線程,也可能是其他某個線程,其實現如下:

  public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

Unsafe的unpark方法也是一個本地方法,其實現如下:

UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread))
  UnsafeWrapper("Unsafe_Unpark");
  Parker* p = NULL;
  if (jthread != NULL) {
    //獲取關聯的Thread實例oop
    oop java_thread = JNIHandles::resolve_non_null(jthread);
    if (java_thread != NULL) {
      //
      jlong lp = java_lang_Thread::park_event(java_thread);
      if (lp != 0) {
        //不爲空
        p = (Parker*)addr_from_java(lp);
      } else {
        //如果爲空,說明是第一次訪問
        //獲取鎖Threads_lock
        MutexLocker mu(Threads_lock);
        //獲取關聯的Thread實例oop
        java_thread = JNIHandles::resolve_non_null(jthread);
        if (java_thread != NULL) {
          //獲取關聯的JavaThread
          JavaThread* thr = java_lang_Thread::thread(java_thread);
          if (thr != NULL) {
            p = thr->parker();
            if (p != NULL) { 
              //設置Thread實例的nativeParkEventPointer屬性,這樣下次調用unpark方法時可以快速的獲取parker指針
              java_lang_Thread::set_park_event(java_thread, addr_to_java(p));
            }
          }
        }
      }
    }
  }
  if (p != NULL) {
    //執行unpark方法喚醒目標線程
    p->unpark();
  }
UNSAFE_END

jlong java_lang_Thread::park_event(oop java_thread) {
  if (_park_event_offset > 0) {
    //即獲取Thread實例的nativeParkEventPointer屬性,該屬性是private的,用來保存關聯的Parker的指針
    return java_thread->long_field(_park_event_offset);
  }
  return 0;
}

bool java_lang_Thread::set_park_event(oop java_thread, jlong ptr) {
  if (_park_event_offset > 0) {
    java_thread->long_field_put(_park_event_offset, ptr);
    return true;
  }
  return false;
}

  Unsafe_Unpark會將JavaThread的parker屬性緩存到Thread實例的nativeParkEventPointer屬性中,方便下次調用unpark方法時可以快速獲取關聯的parker,然後執行unpark方法喚醒目標線程。

二、Parker

 1、定義

       Parker的定義在hotspot\src\share\vm\runtime\park.hpp中,繼承自os::PlatformParker,其包含的屬性如下:

  •   volatile int _counter ; //用來表示Parker的狀態,park方法執行完成爲0,unpark方法執行完成爲1,其中等於1說是一個非常短暫的狀態,一旦線程被環境又會將其置爲0
  •   Parker * FreeNext ; //下一個空閒的Parker
  •   JavaThread * AssociatedWith ; //關聯的線程
  •   static Parker * volatile FreeList ; //空閒的Parker鏈表
  •   static volatile int ListLock ; //操作FreeList的鎖

        父類PlatformParker包含的屬性如下:

  •     int _cur_index;  // 當前使用的_cond索引
  •     pthread_mutex_t _mutex [1] ; //等待的鎖
  •     pthread_cond_t  _cond  [2] ; // 等待的條件,等待一段時間時使用

2、Allocate / Release

     Allocate方法會創建一個Parker,主要是線程創建時調用;Release方法會釋放一個Parker,主要是線程銷燬的時候調用,其調用鏈如下:

 

這兩方法的實現如下:

Parker * Parker::Allocate (JavaThread * t) {
  guarantee (t != NULL, "invariant") ;
  Parker * p ;

  //獲取鎖ListLock
  Thread::SpinAcquire(&ListLock, "ParkerFreeListAllocate");
  {
    //獲取鏈表頭
    p = FreeList;
    if (p != NULL) {
      //將鏈表頭從鏈表中移除
      FreeList = p->FreeNext;
    }
  }
  //釋放鎖
  Thread::SpinRelease(&ListLock);

  if (p != NULL) {
    //有空閒的Parker
    guarantee (p->AssociatedWith == NULL, "invariant") ;
  } else {
    //沒有空閒的,重新創建一個
    p = new Parker() ;
  }
  //保存關聯的線程
  p->AssociatedWith = t ;          // Associate p with t
  p->FreeNext       = NULL ;
  return p ;
}


void Parker::Release (Parker * p) {
  if (p == NULL) return ;
  guarantee (p->AssociatedWith != NULL, "invariant") ;
  guarantee (p->FreeNext == NULL      , "invariant") ;
  //關聯的線程置爲NULL
  p->AssociatedWith = NULL ;
  //獲取鎖
  Thread::SpinAcquire(&ListLock, "ParkerFreeListRelease");
  {
    //將p插入到鏈表頭
    p->FreeNext = FreeList;
    FreeList = p;
  }
  //釋放鎖
  Thread::SpinRelease(&ListLock);
}

Parker() : PlatformParker() {
    _counter       = 0 ;
    FreeNext       = NULL ;
    AssociatedWith = NULL ;
  }

PlatformParker() {
      int status;
      //初始化_cond數組和_mutex
      status = pthread_cond_init (&_cond[REL_INDEX], os::Linux::condAttr());
      assert_status(status == 0, status, "cond_init rel");
      status = pthread_cond_init (&_cond[ABS_INDEX], NULL);
      assert_status(status == 0, status, "cond_init abs");
      status = pthread_mutex_init (_mutex, NULL);
      assert_status(status == 0, status, "mutex_init");
      _cur_index = -1; // mark as unused
    }

注意Parker是JavaThread的一個實例屬性,Unsafe中park和unpark方法都是針對當前線程,即不存在兩個不同的線程訪問同一個Parker實例的情形,但是存在同一個Parker的park/unpark方法在同一個線程內被多次調用。

3、park

     park用於讓某個線程處於阻塞狀態,底層實現是pthread_cond_wait或者pthread_cond_timedwait,其實現如下:

void Parker::park(bool isAbsolute, jlong time) {
  //將_counter屬性置爲0,返回值大於0,說明正在執行unpark動作喚醒當前線程,再park讓其休眠無意義
  if (Atomic::xchg(0, &_counter) > 0) return;
  
  //獲取當前的JavaThread
  Thread* thread = Thread::current();
  assert(thread->is_Java_thread(), "Must be JavaThread");
  JavaThread *jt = (JavaThread *)thread;

  //如果線程已經中斷
  if (Thread::is_interrupted(thread, false)) {
    return;
  }

  timespec absTime;
  if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at all
    return;
  }
  if (time > 0) {
    //初始化absTime,計算等待到那個時間爲止
    unpackTime(&absTime, isAbsolute, time);
  }

  //切換線程狀態爲_thread_blocked,會檢查安全點
  ThreadBlockInVM tbivm(jt);

  //如果該線程已經中斷或者嘗試獲取鎖失敗則返回,嘗試獲取鎖失敗說明有其他線程佔用這個鎖
  if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) {
    return;
  }

  int status ;
  if (_counter > 0)  { //跟一開始的xchg邏輯相同
    _counter = 0;
    //解鎖
    status = pthread_mutex_unlock(_mutex);
    assert (status == 0, "invariant") ;
    //讓修改立即生效
    OrderAccess::fence();
    return;
  }

  //修改線程狀態爲CONDVAR_WAIT
  OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
  jt->set_suspend_equivalent();
  
  assert(_cur_index == -1, "invariant");
  if (time == 0) {
    _cur_index = REL_INDEX; // arbitrary choice when not timed
    //無期限等待,直到被喚醒
    status = pthread_cond_wait (&_cond[_cur_index], _mutex) ;
  } else {
    _cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
    //底層是pthread_cond_timedwait,讓當前線程在_mutex上等待指定的時間,如果這段時間範圍內被喚醒了則返回0,否則返回非0值
    status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime) ;
    //WorkAroundNPTLTimedWaitHang的默認值是1
    if (status != 0 && WorkAroundNPTLTimedWaitHang) {
      //銷燬並重新初始化_cur_index對應的_cond
      pthread_cond_destroy (&_cond[_cur_index]) ;
      pthread_cond_init    (&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr());
    }
  }
  //線程被喚醒了,此時counter會被置爲1
  _cur_index = -1;
  assert_status(status == 0 || status == EINTR ||
                status == ETIME || status == ETIMEDOUT,
                status, "cond_timedwait");
  //將counter重置爲0
  _counter = 0 ;
  //解鎖
  status = pthread_mutex_unlock(_mutex) ;
  assert_status(status == 0, status, "invariant") ;
  OrderAccess::fence();

  if (jt->handle_special_suspend_equivalent_condition()) {
    jt->java_suspend_self();
  }
}

4、unpark

     unpark用於將某個處於休眠狀態的線程喚醒,其實現如下:

void Parker::unpark() {
  int s, status ;
  //加鎖
  status = pthread_mutex_lock(_mutex);
  assert (status == 0, "invariant") ;
  s = _counter;
  //正常unpark完成counter等於1,park完成counter等於0
  _counter = 1;
  if (s < 1) {
    // thread might be parked
    if (_cur_index != -1) {
      //WorkAroundNPTLTimedWaitHang默認值爲1
      if (WorkAroundNPTLTimedWaitHang) {
        //發送信號,喚醒目標線程
        status = pthread_cond_signal (&_cond[_cur_index]);
        assert (status == 0, "invariant");
        //解鎖
        status = pthread_mutex_unlock(_mutex);
        assert (status == 0, "invariant");
      } else {
        int index = _cur_index;
        //先解鎖
        status = pthread_mutex_unlock(_mutex);
        assert (status == 0, "invariant");
        //再喚醒
        status = pthread_cond_signal (&_cond[index]);
        assert (status == 0, "invariant");
      }
    } else {
      //_cur_index等於-1,線程從休眠狀態被喚醒後就是-1了
      pthread_mutex_unlock(_mutex);
      assert (status == 0, "invariant") ;
    }
  } else {
    //_counter大於或者等於1,說明其已經執行過unpark了,不需要再次喚醒了
    pthread_mutex_unlock(_mutex);
    assert (status == 0, "invariant") ;
  }
}

三、ParkEvent 

 1、定義

      ParkEvent的定義也是在park.hpp中,繼承自os::PlatformEvent,其包含的屬性如下:

  •     ParkEvent * FreeNext ; //鏈表中下一個空閒的ParkEvent
  •     Thread * AssociatedWith ; //關聯的線程
  •     intptr_t RawThreadIdentity ;   //下面幾個屬性都是用來實現JVM自身使用的鎖Monitor和Mutex
  •     volatile int Incarnation ;
  •     void * LastWaker ;
  •     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
  •     static ParkEvent * volatile FreeList ; //空閒的ParkEvent鏈表
  •     static volatile int ListLock ; //操作FreeList 的鎖

 PlatformEvent包含的屬性如下:

  •     double CachePad [4] ;   // 和下面的PostPad  一樣,都是爲了規避緩存行問題
  •     volatile int _Event ; //只有兩個值,0表示執行完park,1表示執行完unpark
  •     volatile int _nParked ; //只有兩個值,1或者0,用來記錄park的線程數
  •     pthread_mutex_t _mutex  [1] ; //等待的鎖
  •     pthread_cond_t  _cond   [1] ; //等待的條件
  •     double PostPad  [2] ;
  •     Thread * _Assoc ; //

2、Allocate / Release

     同Parker,這兩方法分別用於分配和銷燬一個ParkEvent,其調用鏈如下:

 

 其實現如下:

ParkEvent * ParkEvent::Allocate (Thread * t) {
  ParkEvent * ev ;

  //獲取鎖
  Thread::SpinAcquire(&ListLock, "ParkEventFreeListAllocate");
  {
    //將FreeList鏈表頭從鏈表中移除
    ev = FreeList;
    if (ev != NULL) {
      FreeList = ev->FreeNext;
    }
  }
  //釋放鎖
  Thread::SpinRelease(&ListLock);

  if (ev != NULL) {
    //如果找到一個空閒的ParkEvent
    guarantee (ev->AssociatedWith == NULL, "invariant") ;
  } else {
    //如果沒有找到,則創建一個
    ev = new ParkEvent () ;
    guarantee ((intptr_t(ev) & 0xFF) == 0, "invariant") ;
  }
  //將_Event置爲0
  ev->reset() ;                     // courtesy to caller
  //保存關聯的線程
  ev->AssociatedWith = t ;          // Associate ev with t
  ev->FreeNext       = NULL ;
  return ev ;
}

void ParkEvent::Release (ParkEvent * ev) {
  if (ev == NULL) return ;
  guarantee (ev->FreeNext == NULL      , "invariant") ;
  ev->AssociatedWith = NULL ;
  //獲取鎖
  Thread::SpinAcquire(&ListLock, "ParkEventFreeListRelease");
  {  
    //歸還到FreeList鏈表中
    ev->FreeNext = FreeList;
    FreeList = ev;
  }
  //釋放鎖
  Thread::SpinRelease(&ListLock);
}

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

PlatformEvent() {
      int status;
      //初始化_cond和_mutex
      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 ;
    }

 void reset() { _Event = 0 ; }

 ParkEvent在JavaThread中也是實例屬性,如下:

3、TryPark / park

      park用於將某個線程變成阻塞狀態,其實現如下:

int os::PlatformEvent::TryPark() {
  for (;;) {
    const int v = _Event ;
    //_Event只能是0或者1
    guarantee ((v == 0) || (v == 1), "invariant") ;
    //將_Event原子的置爲0
    if (Atomic::cmpxchg (0, &_Event, v) == v) return v  ;
  }
}

void os::PlatformEvent::park() {       // AKA "down()"
  int v ;
  for (;;) {
      v = _Event ;
      //將其原子的減1
      if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ;
  }
  guarantee (v >= 0, "invariant") ;
  if (v == 0) {
     //獲取鎖
     int status = pthread_mutex_lock(_mutex);
     assert_status(status == 0, status, "mutex_lock");
     guarantee (_nParked == 0, "invariant") ;
     //已park線程計數加1
     ++ _nParked ;
     //_Event已經原子的減1,變成-1了
     while (_Event < 0) {
        //無期限等待
        status = pthread_cond_wait(_cond, _mutex);
        if (status == ETIME) { status = EINTR; }
        assert_status(status == 0 || status == EINTR, status, "cond_wait");
     }
     //被喚醒了
     //計數減1
     -- _nParked ;
    //重置成0
    _Event = 0 ;
    //釋放鎖
     status = pthread_mutex_unlock(_mutex);
     assert_status(status == 0, status, "mutex_unlock");
    //讓修改立即生效
    OrderAccess::fence();
  }
  guarantee (_Event >= 0, "invariant") ;
}

int os::PlatformEvent::park(jlong millis) {
  guarantee (_nParked == 0, "invariant") ;

  int v ;
  for (;;) {
      v = _Event ;
      //將其原子的減1
      if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ;
  }
  guarantee (v >= 0, "invariant") ;
  if (v != 0) return OS_OK ;

  //計算等待的時間
  struct timespec abst;
  compute_abstime(&abst, millis);

  int ret = OS_TIMEOUT;
  //獲取鎖
  int status = pthread_mutex_lock(_mutex);
  assert_status(status == 0, status, "mutex_lock");
  guarantee (_nParked == 0, "invariant") ;
  //計數加1
  ++_nParked ;

  while (_Event < 0) {
    //讓線程休眠,底層是pthread_cond_timedwait
    status = os::Linux::safe_cond_timedwait(_cond, _mutex, &abst);
    //WorkAroundNPTLTimedWaitHang的值默認是1
    if (status != 0 && WorkAroundNPTLTimedWaitHang) {
      pthread_cond_destroy (_cond);
      pthread_cond_init (_cond, os::Linux::condAttr()) ;
    }
    //被中斷後就返回EINTR,正常被喚醒就返回0,另外兩個是等待超時
    assert_status(status == 0 || status == EINTR ||
                  status == ETIME || status == ETIMEDOUT,
                  status, "cond_timedwait");
    //FilterSpuriousWakeups默認是true              
    if (!FilterSpuriousWakeups) break ;                 // previous semantics
    //如果超時了則退出循環
    if (status == ETIME || status == ETIMEDOUT) break ;
  }
  --_nParked ;
  if (_Event >= 0) {
     ret = OS_OK;
  }
  _Event = 0 ;
  //解鎖
  status = pthread_mutex_unlock(_mutex);
  assert_status(status == 0, status, "mutex_unlock");
  assert (_nParked == 0, "invariant") ;
  //讓修改立即生效
  OrderAccess::fence();
  return ret;
}

4、unpark

      unpark用於喚醒某個被park方法阻塞的線程,其實現如下:

void os::PlatformEvent::unpark() {
  //將其原子的置爲1,如果原來就是1,說明已經unpark過了,直接返回
  if (Atomic::xchg(1, &_Event) >= 0) return;

  //獲取鎖
  int status = pthread_mutex_lock(_mutex);
  assert_status(status == 0, status, "mutex_lock");
  int AnyWaiters = _nParked;
  assert(AnyWaiters == 0 || AnyWaiters == 1, "invariant");
  //WorkAroundNPTLTimedWaitHang默認是true
  if (AnyWaiters != 0 && WorkAroundNPTLTimedWaitHang) {
    AnyWaiters = 0;
    //發信號喚醒該線程,被喚醒後將_nParked置爲0
    pthread_cond_signal(_cond);
  }
  //釋放鎖
  status = pthread_mutex_unlock(_mutex);
  assert_status(status == 0, status, "mutex_unlock");
  if (AnyWaiters != 0) {
    //pthread_cond_signal不要求獲取鎖,此處再次喚醒
    status = pthread_cond_signal(_cond);
    assert_status(status == 0, status, "cond_signal");
  }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章