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类方法有多个版本,如下:

Hotspot Parker和ParkEvent 源码解析_第1张图片

其用途都是让某个线程处于阻塞(休眠)状态,操作系统不会再给该线程分配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(obj) : 0);
    event.commit();
  }
UNSAFE_END

JavaThreadParkedState通过构造方法和析构方法来修改线程的状态,并记录锁等待的次数和耗时,其实现如下:

Hotspot Parker和ParkEvent 源码解析_第2张图片

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,主要是线程销毁的时候调用,其调用链如下:

Hotspot Parker和ParkEvent 源码解析_第3张图片

 

这两方法的实现如下:

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,其调用链如下:

Hotspot Parker和ParkEvent 源码解析_第4张图片

Hotspot Parker和ParkEvent 源码解析_第5张图片 

 其实现如下:

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");
  }
}

 

你可能感兴趣的:(Hotspot和Linux内核,LockSupport,Parker,ParkEvent,park,unpark)