Synchronized鎖了個什么玩意
微信公眾號:saikou4java
問題或建議,請公眾號留言;
Synchronized鎖了個什么玩意
先大膽假設,如果讓我們自己實現(xiàn)一個鎖一樣的功能,應該怎么實現(xiàn)呢。某個資源,我在使用,別人就不能用,必須等我使用完畢才能用,假設使用全局變量來控制。
使用偽代碼完成上面的邏輯
//?定義一個靜態(tài)變量
static?boolean?isLock?=?false;
//?加鎖的方法
public?static?void?lock()?{
????while?(true)?{
????????//?如果鎖是打開的,獲取并關閉鎖,否則循環(huán)等待
????????if?(!isLock)?{
????????????isLock?=?true;
????????????break;
????????}
????}
}
//?解鎖的方法
private?static?void?release()?{
????isLock?=?false;
}
偽代碼是不可用的,可以猜想一下synchronized是不是也是通過某個標志來控制的呢,我們寫一個方法,通過生成的.class文件來查看一下
public?static?void?main(String[]?args)?{
????int?a?=?0;
????synchronized?(App3.class){
????????a++;
????}
}
對應的.class文件反編譯為
public?static?void?main(String[]?args)?{
????int?a?=?0;
????Class?var2?=?App3.class;
????synchronized(App3.class)?{
????????int?var5?=?a?+?1;
????}
}
對應的jvm指令為
public?static?void?main(java.lang.String[]);
???Code:
???????0:?iconst_0?????// a值入棧
???????1:?istore_1?????//?a值出棧,保存到局部變量中
???????2:?ldc??????????//?常量池中的引用var2入棧
???????4:?dup??????????//?復制棧頂?shù)膙ar2,再次入棧
???????5:?astore_2?????//?var2出棧,保存到局部變量中
???????6:?monitorenter????//?var2出棧,獲取對象監(jiān)視器
???????7:?iinc??1,?1???????//?局部變量a加1
??????10:?aload_2???????//?從局部變量中獲取var2,再次入棧
??????11:?monitorexit????//?var2出棧,釋放并退出對象監(jiān)視器
??????12:?goto??20?????????//?無條件跳轉到指定位置
??????...????????????????//??異常處理
??????20:?return??????????//?void函數(shù)返回
從上面的指令可以看出,synchronized關鍵字轉化為了指令monitorenter和monitorexit,那么我們只需要知道這兩條指令是什么意思就行了。
monitorenter和monitorexit是什么
java虛擬機規(guī)范中對monitorenter描述為:Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref.
谷歌翻譯一下就是:每個object都與一個monitor關聯(lián)。monitor只有在擁有所有者的情況下才會被鎖定。執(zhí)行monitorenter的線程會嘗試獲得與objectref關聯(lián)的監(jiān)視器的所有權。
那么我們有兩個問題
monitors是個什么東西
monitor和object是怎么關聯(lián)的
首先看monitors是什么東西,在軟件的世界里,只有代碼,既然jvm也是一個軟件,那我們找到monitors的代碼就知道了。這個代碼在openjdk11\src\hotspot\share\runtime包下,有個類叫objectMonitor.cpp。雖然是個2446行的c++代碼,完全看不懂,但是只要找到關鍵方法就可以,此處可以求助陳師兄--!。
void?ObjectMonitor::enter(TRAPS)?{
??????//?看到Thread倍感情切,也就能看懂這個單詞了。
??????//?定義當前線程self
??????Thread?*?const?Self?=?THREAD;
??????//?和java中的cas類似,將對象的owner設置為當前線程self
??????//?這一步就是用來獲取鎖的
??????void?*?cur?=?Atomic::cmpxchg(Self,?&_owner,?(void*)NULL);
??????//?獲取鎖失敗
??????if?(cur?==?NULL)?{
????????assert(_recursions?==?0,?"invariant");
????????assert(_owner?==?Self,?"invariant");
????????return;
??????}
??????//?同一個線程可以重復獲取鎖
??????//?_recursions記錄獲取鎖的次數(shù)
??????if?(cur?==?Self)?{
????????_recursions++;
????????return;
??????}
??????//?第一次獲取鎖成功進行初始化,設置了_recursions
??????//?斷言只有在_recursions==0的時候才可以獲取鎖
??????if?(Self->is_lock_owned?((address)cur))?{
????????assert(_recursions?==?0,?"internal?state?error");
????????_recursions?=?1;
????????_owner?=?Self;
????????return;
??????}
??????assert(Self->_Stalled?==?0,?"invariant");
??????Self->_Stalled?=?intptr_t(this);
??????if?(Knob_SpinEarly?&&?TrySpin?(Self)?>?0)?{
????????assert(_owner?==?Self,?"invariant");
????????assert(_recursions?==?0,?"invariant");
????????assert(((oop)(object()))->mark()?==?markOopDesc::encode(this),?"invariant");
????????Self->_Stalled?=?0;
????????return;
??????}
??????assert(_owner?!=?Self,?"invariant");
??????assert(_succ?!=?Self,?"invariant");
??????assert(Self->is_Java_thread(),?"invariant");
??????JavaThread?*?jt?=?(JavaThread?*)?Self;
??????assert(!SafepointSynchronize::is_at_safepoint(),?"invariant");
??????assert(jt->thread_state()?!=?_thread_blocked,?"invariant");
??????assert(this->object()?!=?NULL,?"invariant");
??????assert(_count?>=?0,?"invariant");
??????Atomic::inc(&_count);
??????JFR_ONLY(JfrConditionalFlushWithStacktrace<EventJavaMonitorEnter>?flush(jt);)
??????EventJavaMonitorEnter?event;
??????if?(event.should_commit())?{
????????event.set_monitorClass(((oop)this->object())->klass());
????????event.set_address((uintptr_t)(this->object_addr()));
??????}
??????{
????????JavaThreadBlockedOnMonitorEnterState?jtbmes(jt,?this);
????????Self->set_current_pending_monitor(this);
????????DTRACE_MONITOR_PROBE(contended__enter,?this,?object(),?jt);
????????if?(JvmtiExport::should_post_monitor_contended_enter())?{
??????????JvmtiExport::post_monitor_contended_enter(jt,?this);
????????}
????????OSThreadContendState?osts(Self->osthread());
????????ThreadBlockInVM?tbivm(jt);
????????for?(;;)?{
??????????jt->set_suspend_equivalent();
??????????EnterI(THREAD);
??????????if?(!ExitSuspendEquivalent(jt))?break;
??????????_recursions?=?0;
??????????_succ?=?NULL;
??????????exit(false,?Self);
??????????jt->java_suspend_self();
????????}
????????Self->set_current_pending_monitor(NULL);
??????}
??????Atomic::dec(&_count);
??????assert(_count?>=?0,?"invariant");
??????Self->_Stalled?=?0;
??????assert(_recursions?==?0,?"invariant");
??????assert(_owner?==?Self,?"invariant");
??????assert(_succ?!=?Self,?"invariant");
??????assert(((oop)(object()))->mark()?==?markOopDesc::encode(this),?"invariant");
??????DTRACE_MONITOR_PROBE(contended__entered,?this,?object(),?jt);
??????if?(JvmtiExport::should_post_monitor_contended_entered())?{
????????JvmtiExport::post_monitor_contended_entered(jt,?this);
??????}
??????if?(event.should_commit())?{
????????event.set_previousOwner((uintptr_t)_previous_owner_tid);
????????event.commit();
??????}
??????OM_PERFDATA_OP(ContendedLockAttempts,?inc());
????}
雖然上面的方法完全看不懂,但是我們已經(jīng)好像得到了想要的東西,monitor通過cas的方式把owner設置為當前線程,如果owner已經(jīng)被其他線程設置過了,直接設置失敗,即獲取鎖失敗,好像跟我們偽代碼里寫的那個isLock作用一樣,原來jvm你也搞了個變量來控制。在第一次獲取鎖成功的時候,設置了recursions = 1,并且斷言了只有recursions = 0的時候才可以獲取成功,為了保證鎖可以被一個線程獲取多次,使用了recursions ++,可以猜測,釋放鎖肯定使用了recursions --,直到減為0,也就是說一個線程獲取的鎖必須全部釋放了,其他線程才有可能調用添加鎖的方法。
再來看看釋放鎖的方法
void?ObjectMonitor::exit(bool?not_suspended,?TRAPS)?{
??Thread?*?const?Self?=?THREAD;
??//?先判斷當前線程是否等于owner
??//?有兩種情況
??if?(THREAD?!=?_owner)?{
????//?輕量級鎖情況的釋放
????if?(THREAD->is_lock_owned((address)?_owner))?{
??????assert(_recursions?==?0,?"invariant");
??????_owner?=?THREAD;
??????_recursions?=?0;
????}?else?{
??????//?真的不是一個線程,直接退出
??????//?會拋出IllegalMonitorStateException異常
??????//?某個線程的鎖不能由其他線程釋放
??????TEVENT(Exit?-?Throw?IMSX);
??????assert(false,?"Non-balanced?monitor?enter/exit!?Likely?JNI?locking");
??????return;
????}
??}
??if?(_recursions?!=?0)?{
????//?加多少次鎖就釋放多少次
????_recursions--;
????TEVENT(Inflated?exit?-?recursive);
????return;
??}
??if?((SyncFlags?&?4)?==?0)?{
????_Responsible?=?NULL;
??}
#if?INCLUDE_JFR
??if?(not_suspended?&&?EventJavaMonitorEnter::is_enabled())?{
????_previous_owner_tid?=?JFR_THREAD_ID(Self);
??}
#endif
??for?(;;)?{
????assert(THREAD?==?_owner,?"invariant");
????if?(Knob_ExitPolicy?==?0)?{
??????//?這一步就是用來釋放鎖的,看方法名字
??????OrderAccess::release_store(&_owner,?(void*)NULL);
??????OrderAccess::storeload();?
??????if?((intptr_t(_EntryList)|intptr_t(_cxq))?==?0?||?_succ?!=?NULL)?{
????????TEVENT(Inflated?exit?-?simple?egress);
????????return;
??????}
??????TEVENT(Inflated?exit?-?complex?egress);
??????if?(!Atomic::replace_if_null(THREAD,?&_owner))?{
????????return;
??????}
??????TEVENT(Exit?-?Reacquired);
????}?else?{
??????if?((intptr_t(_EntryList)|intptr_t(_cxq))?==?0?||?_succ?!=?NULL)?{
????????OrderAccess::release_store(&_owner,?(void*)NULL);
????????OrderAccess::storeload();
????????if?(_cxq?==?NULL?||?_succ?!=?NULL)?{
??????????TEVENT(Inflated?exit?-?simple?egress);
??????????return;
????????}
????????if?(!Atomic::replace_if_null(THREAD,?&_owner))?{
??????????TEVENT(Inflated?exit?-?reacquired?succeeded);
??????????return;
????????}
????????TEVENT(Inflated?exit?-?reacquired?failed);
??????}?else?{
????????TEVENT(Inflated?exit?-?complex?egress);
??????}
????}
????guarantee(_owner?==?THREAD,?"invariant");
????ObjectWaiter?*?w?=?NULL;
????int?QMode?=?Knob_QMode;
????if?(QMode?==?2?&&?_cxq?!=?NULL)?{
??????w?=?_cxq;
??????assert(w?!=?NULL,?"invariant");
??????assert(w->TState?==?ObjectWaiter::TS_CXQ,?"Invariant");
??????ExitEpilog(Self,?w);
??????return;
????}
????if?(QMode?==?3?&&?_cxq?!=?NULL)?{
??????w?=?_cxq;
??????for?(;;)?{
????????assert(w?!=?NULL,?"Invariant");
????????ObjectWaiter?*?u?=?Atomic::cmpxchg((ObjectWaiter*)NULL,?&_cxq,?w);
????????if?(u?==?w)?break;
????????w?=?u;
??????}
??????assert(w?!=?NULL,?"invariant");
??????ObjectWaiter?*?q?=?NULL;
??????ObjectWaiter?*?p;
??????for?(p?=?w;?p?!=?NULL;?p?=?p->_next)?{
????????guarantee(p->TState?==?ObjectWaiter::TS_CXQ,?"Invariant");
????????p->TState?=?ObjectWaiter::TS_ENTER;
????????p->_prev?=?q;
????????q?=?p;
??????}
??????ObjectWaiter?*?Tail;
??????for?(Tail?=?_EntryList;?Tail?!=?NULL?&&?Tail->_next?!=?NULL;
???????????Tail?=?Tail->_next)
??????if?(Tail?==?NULL)?{
????????_EntryList?=?w;
??????}?else?{
????????Tail->_next?=?w;
????????w->_prev?=?Tail;
??????}
????}
????if?(QMode?==?4?&&?_cxq?!=?NULL)?{
??????w?=?_cxq;
??????for?(;;)?{
????????assert(w?!=?NULL,?"Invariant");
????????ObjectWaiter?*?u?=?Atomic::cmpxchg((ObjectWaiter*)NULL,?&_cxq,?w);
????????if?(u?==?w)?break;
????????w?=?u;
??????}
??????assert(w?!=?NULL,?"invariant");
??????ObjectWaiter?*?q?=?NULL;
??????ObjectWaiter?*?p;
??????for?(p?=?w;?p?!=?NULL;?p?=?p->_next)?{
????????guarantee(p->TState?==?ObjectWaiter::TS_CXQ,?"Invariant");
????????p->TState?=?ObjectWaiter::TS_ENTER;
????????p->_prev?=?q;
????????q?=?p;
??????}
??????if?(_EntryList?!=?NULL)?{
????????q->_next?=?_EntryList;
????????_EntryList->_prev?=?q;
??????}
??????_EntryList?=?w;
????}
????w?=?_EntryList;
????if?(w?!=?NULL)?{
??????assert(w->TState?==?ObjectWaiter::TS_ENTER,?"invariant");
??????ExitEpilog(Self,?w);
??????return;
????}
????w?=?_cxq;
????if?(w?==?NULL)?continue;
????for?(;;)?{
??????assert(w?!=?NULL,?"Invariant");
??????ObjectWaiter?*?u?=?Atomic::cmpxchg((ObjectWaiter*)NULL,?&_cxq,?w);
??????if?(u?==?w)?break;
??????w?=?u;
????}
????TEVENT(Inflated?exit?-?drain?cxq?into?EntryList);
????assert(w?!=?NULL,?"invariant");
????assert(_EntryList?==?NULL,?"invariant");
????if?(QMode?==?1)?{
??????ObjectWaiter?*?s?=?NULL;
??????ObjectWaiter?*?t?=?w;
??????ObjectWaiter?*?u?=?NULL;
??????while?(t?!=?NULL)?{
????????guarantee(t->TState?==?ObjectWaiter::TS_CXQ,?"invariant");
????????t->TState?=?ObjectWaiter::TS_ENTER;
????????u?=?t->_next;
????????t->_prev?=?u;
????????t->_next?=?s;
????????s?=?t;
????????t?=?u;
??????}
??????_EntryList??=?s;
??????assert(s?!=?NULL,?"invariant");
????}?else?{
??????_EntryList?=?w;
??????ObjectWaiter?*?q?=?NULL;
??????ObjectWaiter?*?p;
??????for?(p?=?w;?p?!=?NULL;?p?=?p->_next)?{
????????guarantee(p->TState?==?ObjectWaiter::TS_CXQ,?"Invariant");
????????p->TState?=?ObjectWaiter::TS_ENTER;
????????p->_prev?=?q;
????????q?=?p;
??????}
????}
????if?(_succ?!=?NULL)?continue;
????w?=?_EntryList;
????if?(w?!=?NULL)?{
??????guarantee(w->TState?==?ObjectWaiter::TS_ENTER,?"invariant");
??????ExitEpilog(Self,?w);
??????return;
????}
??}
}
基本就認識幾個單詞,再次@陳師兄--!,但是可以看到跟我們猜想的過程一樣,釋放鎖的時候先recursions--,再把owner重置。所以synchronized關鍵字鎖的是個什么東西呢,鎖的是object,鎖的是object關聯(lián)的monitor,鎖的是object關聯(lián)的monitor里面的owner,都說得過去。
那monitor和object是怎么關聯(lián)的
java對象之間的關系,有組合有引用有繼承有實現(xiàn),但是我們從來沒有見過一個monitor這樣的類,猜測一下,如果我們想讓一個對象關聯(lián)另一個對象,傳入另一個對象的引用就行了,引用就是這個對象在內存中的地址值,所以我們的對象可能也在某個地方保存了monitor的地址值,要搞清楚這個問題,必須知道對象里的結構是什么。
根據(jù)《深入理解java虛擬機》,在HotSpot虛擬機中,對象在內存中存儲的布局可以分為3個區(qū)域,對象頭(Header),實例數(shù)據(jù)(Instance Data),對齊填充(Padding)
Header又分為兩部分,第一部分用于存儲對象自身的運行時數(shù)據(jù),如哈希碼,GC分帶年齡,鎖狀態(tài)標志,線程持有的鎖,偏向線程ID,偏向時間戳等,這部分官方稱為Mark Word。第二部分是類型指針,即對象指向它的類元數(shù)據(jù)的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。
關于對象頭中的信息可以查看 openJdk中 markOop.hpp的注釋。

我們就把對象頭打印出來看看,根據(jù)不同的鎖狀態(tài),對象頭中的數(shù)據(jù)代表的含義會發(fā)生變化,64位intel cpu使用小端存儲,最高2bit10就是上面注釋中的lock,代表對象持有重量級鎖,其余62位代表monitor的指針。
OFFSET??SIZE???TYPE?DESCRIPTION??????????????VALUE
?0?????4????????(object?header)?????????????da?f5?28?1d?(11011010?11110101?00101000?00011101)?(489223642)
?4?????4????????(object?header)?????????????00?00?00?00?(00000000?00000000?00000000?00000000)?(0)
?8?????4????????(object?header)?????????????05?d6?00?f8?(00000101?11010110?00000000?11111000)?(-134162939)
12?????4???????(loss?due?to?the?next?object?alignment)
Instance?size:?16?bytes
Space?losses:?0?bytes?internal?+?4?bytes?external?=?4?bytes?total
后記
以上內存不保證任何正確性,純屬猜測,沒有經(jīng)過證明。
下面的是我的公眾號二維碼圖片,歡迎關注。
