摘要:實現(xiàn)原理虛擬機鎖原理虛擬機中對象頭部信息可以看見對象頭中結(jié)構(gòu)中的成員,允許壓縮。否則,將偏向鎖撤銷,升級為輕量級鎖。存在明顯多線程競爭的場景下使用偏向鎖是不合適的,例如生產(chǎn)者消費者隊列。
synchronied實現(xiàn)原理 虛擬機鎖原理
虛擬機中對象頭部信息
/*hotspot/src/share/vm/oops/oop.hpp*/ class oopDesc { friend class VMStructs; private: volatile markOop _mark; union _metadata { Klass* _klass; narrowKlass _compressed_klass; } _metadata;
可以看見對象頭中結(jié)構(gòu)
Mark Word:instanceOopDesc中的_mark成員,允許壓縮。它用于存儲對象的運行時記錄信息,如哈希值、GC分代年齡(Age)、鎖狀態(tài)標志(偏向鎖、輕量級鎖、重量級鎖)、線程持有的鎖、偏向線程ID、偏向時間戳等
元數(shù)據(jù)指針:instanceOopDesc中的_metadata成員,它是聯(lián)合體,可以表示未壓縮的Klass指針(_klass)和壓縮的Klass指針。對應(yīng)的klass指針指向一個存儲類的元數(shù)據(jù)的Klass對象
32位的對象頭結(jié)構(gòu),64位結(jié)構(gòu)略
// 32 bits: // -------- // hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) // size:32 ------------------------------------------>| (CMS free block) // PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
biased_lock 表示是否偏向鎖
lock類型
00 locked 輕量級鎖
01 unlocked 無鎖
10 monitor 排他鎖
11 marked 標記
加鎖過程代碼片段
// 默認嘗試偏向鎖 void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) { //是否使用偏向鎖 if (UseBiasedLocking) { //未到達safepoint,嘗試重偏向 if (!SafepointSynchronize::is_at_safepoint()) { BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD); if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) { return; } } else { //在safepoint進行撤銷偏向鎖 assert(!attempt_rebias, "can not rebias toward VM thread"); BiasedLocking::revoke_at_safepoint(obj); } //如果走到這里則說明偏向鎖已撤銷,進行slow_enter(加輕量級鎖) assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); } slow_enter (obj, lock, THREAD) ; } // 輕量級鎖 void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) { markOop mark = obj->mark(); assert(!mark->has_bias_pattern(), "should not see bias pattern here"); if (mark->is_neutral()) { //如果無鎖狀態(tài) // 在lock對象上設(shè)置displaced mark word lock->set_displaced_header(mark); //使用CAS操作交換lock和object的mark word if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) { TEVENT (slow_enter: release stacklock) ; return ; } // 如果CAS失敗,則跳到下面inflate(重量級鎖) } else //如果給相同對象加鎖,則后續(xù)的鎖的displaced mark設(shè)置為NULL(不會重復(fù)上鎖) if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { assert(lock != mark->locker(), "must not re-lock the same lock"); assert(lock != (BasicLock*)obj->mark(), "don"t relock with same BasicLock"); lock->set_displaced_header(NULL); return; } //標記lock對象為unused,后續(xù)由CMS回收,并調(diào)用inflate(重量級鎖) lock->set_displaced_header(markOopDesc::unused_mark()); ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD); }偏向鎖
在大多數(shù)情況下,線程之間不存在競爭關(guān)系,即一個鎖會被某個線程多次使用。如果每次都需要申請鎖,開銷會比較大。因此出現(xiàn)了偏向鎖,獲取偏向鎖之后,如果是不存在其他線程競爭鎖,那么就不需要調(diào)用CAS來獲取鎖,以達到減少I/O的目的
加鎖過程case 1:當該對象第一次被線程獲得鎖的時候,發(fā)現(xiàn)是匿名偏向狀態(tài),則會用CAS指令,將mark word中的thread id由0改成當前線程Id。如果成功,則代表獲得了偏向鎖,繼續(xù)執(zhí)行同步塊中的代碼。否則,將偏向鎖撤銷,升級為輕量級鎖。
case 2:當被偏向的線程再次進入同步塊時,發(fā)現(xiàn)鎖對象偏向的就是當前線程,在通過一些額外的檢查后(細節(jié)見后面的文章),會往當前線程的棧中添加一條Displaced Mark Word為空的Lock Record中,然后繼續(xù)執(zhí)行同步塊的代碼,因為操縱的是線程私有的棧,因此不需要用到CAS指令;由此可見偏向鎖模式下,當被偏向的線程再次嘗試獲得鎖時,僅僅進行幾個簡單的操作就可以了,在這種情況下,synchronized關(guān)鍵字帶來的性能開銷基本可以忽略。
case 3.當其他線程進入同步塊時,發(fā)現(xiàn)已經(jīng)有偏向的線程了,則會進入到撤銷偏向鎖的邏輯里,一般來說,會在safepoint中去查看偏向的線程是否還存活,如果存活且還在同步塊中則將鎖升級為輕量級鎖,原偏向的線程繼續(xù)擁有鎖,當前線程則走入到鎖升級的邏輯里;如果偏向的線程已經(jīng)不存活或者不在同步塊中,則將對象頭的mark word改為無鎖狀態(tài)(unlocked),之后再升級為輕量級鎖。
由此可見,偏向鎖升級的時機為:當鎖已經(jīng)發(fā)生偏向后,只要有另一個線程嘗試獲得偏向鎖,則該偏向鎖就會升級成輕量級鎖。當然這個說法不絕對,因為還有批量重偏向這一機制。
解鎖過程當有其他線程嘗試獲得鎖時,是根據(jù)遍歷偏向線程的lock record來確定該線程是否還在執(zhí)行同步塊中的代碼。因此偏向鎖的解鎖很簡單,僅僅將棧中的最近一條lock record的obj字段設(shè)置為null。需要注意的是,偏向鎖的解鎖步驟中并不會修改對象頭中的thread id。
批量重偏向與撤銷從上文偏向鎖的加鎖解鎖過程中可以看出,當只有一個線程反復(fù)進入同步塊時,偏向鎖帶來的性能開銷基本可以忽略,但是當有其他線程嘗試獲得鎖時,就需要等到safe point時將偏向鎖撤銷為無鎖狀態(tài)或升級為輕量級/重量級鎖。safe point這個詞我們在GC中經(jīng)常會提到,其代表了一個狀態(tài),在該狀態(tài)下所有線程都是暫停的(大概這么個意思),詳細可以看這篇文章??傊?,偏向鎖的撤銷是有一定成本的,如果說運行時的場景本身存在多線程競爭的,那偏向鎖的存在不僅不能提高性能,而且會導(dǎo)致性能下降。因此,JVM中增加了一種批量重偏向/撤銷的機制。
存在如下兩種情況:(見官方論文第4小節(jié)):
1.一個線程創(chuàng)建了大量對象并執(zhí)行了初始的同步操作,之后在另一個線程中將這些對象作為鎖進行之后的操作。這種case下,會導(dǎo)致大量的偏向鎖撤銷操作。
2.存在明顯多線程競爭的場景下使用偏向鎖是不合適的,例如生產(chǎn)者/消費者隊列。
批量重偏向(bulk rebias)機制是為了解決第一種場景。批量撤銷(bulk revoke)則是為了解決第二種場景。
其做法是:以class為單位,為每個class維護一個偏向鎖撤銷計數(shù)器,每一次該class的對象發(fā)生偏向撤銷操作時,該計數(shù)器+1,當這個值達到重偏向閾值(默認20)時,JVM就認為該class的偏向鎖有問題,因此會進行批量重偏向。每個class對象會有一個對應(yīng)的epoch字段,每個處于偏向鎖狀態(tài)對象的mark word中也有該字段,其初始值為創(chuàng)建該對象時,class中的epoch的值。每次發(fā)生批量重偏向時,就將該值+1,同時遍歷JVM中所有線程的棧,找到該class所有正處于加鎖狀態(tài)的偏向鎖,將其epoch字段改為新值。下次獲得鎖時,發(fā)現(xiàn)當前對象的epoch值和class的epoch不相等,那就算當前已經(jīng)偏向了其他線程,也不會執(zhí)行撤銷操作,而是直接通過CAS操作將其mark word的Thread Id 改成當前線程Id。
當達到重偏向閾值后,假設(shè)該class計數(shù)器繼續(xù)增長,當其達到批量撤銷的閾值后(默認40),JVM就認為該class的使用場景存在多線程競爭,會標記該class為不可偏向,之后,對于該class的鎖,直接走輕量級鎖的流程
product(intx, BiasedLockingBulkRebiasThreshold, 20, "Threshold of number of revocations per type to try to " "rebias all objects in the heap of that type") product(intx, BiasedLockingBulkRevokeThreshold, 40, "Threshold of number of revocations per type to permanently " "revoke biases of all objects in the heap of that type")輕量級鎖
加鎖過程代碼
CASE(_monitorenter): { oop lockee = STACK_OBJECT(-1); // 創(chuàng)建一個空堆對象lockee CHECK_NULL(lockee); // 遍歷stack中的lock對象,尋找是否存在指向?qū)ο鬄榇渔i對象的 BasicObjectLock* limit = istate->monitor_base(); BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base(); BasicObjectLock* entry = NULL; while (most_recent != limit ) { if (most_recent->obj() == NULL) entry = most_recent; else if (most_recent->obj() == lockee) break; most_recent++; } if (entry != NULL) { //已存在鎖對象,構(gòu)建一個無鎖狀態(tài)的Displaced Mark Word //設(shè)置到Lock Record中去 entry->set_obj(lockee); markOop displaced = lockee->mark()->set_unlocked(); entry->lock()->set_displaced_header(displaced); if (Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) { // 如果CAS替換不成功,代表鎖對象不是無鎖狀態(tài),這時候判斷下是不是鎖重入 if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { entry->lock()->set_displaced_header(NULL); } else { // CAS操作失敗則調(diào)用monitorenter CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); } } UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); } else { istate->set_msg(more_monitors); UPDATE_PC_AND_RETURN(0); // Re-execute } }
解鎖過程代碼與加鎖基本相同,省略
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/73021.html
摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級技術(shù)專家我看過哪些技術(shù)類書籍。 大家好,我是...
摘要:表示的是兩個,當其中任意一個計算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)常可見它的使用,在開始分析它的高并發(fā)實現(xiàn)機制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計 分布式,高可用,和機器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個名詞,今天我們首先來說說分布式。 探究...
摘要:純分享直接上干貨操作系統(tǒng)并發(fā)支持進程管理內(nèi)存管理文件系統(tǒng)系統(tǒng)進程間通信網(wǎng)絡(luò)通信阻塞隊列數(shù)組有界隊列鏈表無界隊列優(yōu)先級有限無界隊列延時無界隊列同步隊列隊列內(nèi)存模型線程通信機制內(nèi)存共享消息傳遞內(nèi)存模型順序一致性指令重排序原則內(nèi)存語義線程 純分享 , 直接上干貨! 操作系統(tǒng)并發(fā)支持 進程管理內(nèi)存管...
閱讀 2001·2021-11-19 09:40
閱讀 1961·2021-09-28 09:36
閱讀 2291·2021-09-22 10:02
閱讀 2733·2019-08-30 14:00
閱讀 1962·2019-08-29 15:31
閱讀 2905·2019-08-29 15:11
閱讀 2915·2019-08-29 13:04
閱讀 1088·2019-08-27 10:55