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