<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Synchronized鎖了個什么玩意

          共 11852字,需瀏覽 24分鐘

           ·

          2023-06-30 11:25

          微信公眾號: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)視器的所有權。

          那么我們有兩個問題

          1. monitors是個什么東西

          2. monitorobject是怎么關聯(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,都說得過去。

          monitorobject是怎么關聯(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的注釋。

          1843fb3a1c3ffec726a3a4602de51049.webp
          我們就把對象頭打印出來看看,根據(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)過證明。



          下面的是我的公眾號二維碼圖片,歡迎關注。

          瀏覽 29
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美一级毛片久久99精品蜜桃 | 18成人在线网站 | 亚洲综合伊人 | 久久亚洲国产精品五月天婷婷 | 在线视频福利国产 |