網上有許多講偏向鎖,輕量級鎖的文章,但對偏向鎖如何升級講的不夠明白,有些文章還相互矛盾,經過對jvm源碼(biasedLocking.cpp)的仔細分析和追蹤,基本升級過程有了一個清晰的過程,現將升級流程闡述如下:
因為偏向鎖,鎖住對象時,會寫入對象頭相應的標識,我們先把對象頭(官方叫法為:Mark Word)的圖示如下(借用了網友的圖片):
通過上面的圖片,我們可以知道,對象處於偏向鎖時,mark word中的偏向鎖標記為1,鎖標志位為01;下面是分析過jvm源碼(biasedLocking.cpp)解析的偏向鎖升級流程(忽略一些細節),示例中:線程1當前擁有偏向鎖對象,線程2是需要競爭到偏向鎖。
- 線程2來競爭鎖對象;
- 判斷當前對象頭是否是偏向鎖;
- 判斷擁有偏向鎖的線程1是否還存在;
- 線程1不存在,直接設置偏向鎖標識為0(線程1執行完畢后,不會主動去釋放偏向鎖);
- 使用cas替換偏向鎖線程ID為線程2,鎖不升級,仍為偏向鎖;
- 線程1仍然存在,暫停線程1;
- 設置鎖標志位為00(變為輕量級鎖),偏向鎖為0;
- 從線程1的空閑monitor record中讀取一條,放至線程1的當前monitor record中;
- 更新mark word,將mark word指向線程1中monitor record的指針;
- 繼續執行線程1的代碼;
- 鎖升級為輕量級鎖;
- 線程2自旋來獲取鎖對象;
上面仍有一個問題,即如何判斷線程1已經不存在了?
仍然是分析完jvm源碼(thread.cpp)后,得到的如下結論:
(1) 線程執行start時,會將自己寫入一個thread_list中,這是一個linked結構,有pre和next節點;
對應源碼位置:
void Threads::add(JavaThread* p, bool force_daemon) {
// The threads lock must be owned at this pointassert_locked_or_safepoint(Threads_lock);// See the comment for this method in thread.hpp for its purpose and// why it is called here.p->initialize_queues();p->set_next(_thread_list);_thread_list = p;_number_of_threads++;oop threadObj = p->threadObj();bool daemon = true;// Bootstrapping problem: threadObj can be null for initial// JavaThread (or for threads attached via JNI)if ((!force_daemon) && (threadObj == NULL || !java_lang_Thread::is_daemon(threadObj))) {_number_of_non_daemon_threads++;daemon = false;}p->set_safepoint_visible(true);ThreadService::add_thread(p, daemon);// Possible GC point.
Events::log(p, "Thread added: " INTPTR_FORMAT, p);
}
(2)線程執行完后,會將自己從thread list中清理掉(源碼位置: Threads::remove(this));
因此只需判斷thread list中是否存在線程1即可,判斷源代碼(位於
biasedLocking.cpp中 )如下:
bool thread_is_alive = false;
if (requesting_thread == biased_thread) {
thread_is_alive = true;
} else {
for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) {
if (cur_thread == biased_thread) {
thread_is_alive = true;
break;
}
}