無(wú)標(biāo)題文章

Java的wait()喉脖、notify()學(xué)習(xí)三部曲由三篇文章組成亚隙,內(nèi)容分別是:?

一、通過(guò)閱讀openjdk8的源碼肠牲,分析和理解wait幼衰,notify在JVM中的具體執(zhí)行過(guò)程;?

二缀雳、修改JVM源碼渡嚣,編譯構(gòu)建成新的JVM,把我們感興趣的參數(shù)打印出來(lái),結(jié)合具體代碼檢查和我們的理解是否一致严拒;?

三扬绪、修改JVM源碼,編譯構(gòu)建成新的JVM裤唠,按照我們的理解去修改關(guān)鍵參數(shù),看能否達(dá)到預(yù)期效果莹痢;

現(xiàn)在种蘸,咱們一起開(kāi)始既漫長(zhǎng)又深入的wait、notify學(xué)習(xí)之旅吧竞膳!

wait()和notify()的通常用法

Java多線程開(kāi)發(fā)中航瞭,我們常用到wait()和notify()方法來(lái)實(shí)現(xiàn)線程間的協(xié)作,簡(jiǎn)單的說(shuō)步驟如下:?

1. A線程取得鎖坦辟,執(zhí)行wait()刊侯,釋放鎖;?

2. B線程取得鎖,完成業(yè)務(wù)后執(zhí)行notify()锉走,再釋放鎖;?

3. B線程釋放鎖之后滨彻,A線程取得鎖,繼續(xù)執(zhí)行wait()之后的代碼挪蹭;

關(guān)于synchronize修飾的代碼塊

通常亭饵,對(duì)于synchronize(lock){…}這樣的代碼塊,編譯后會(huì)生成monitorenter和monitorexit指令梁厉,線程執(zhí)行到monitorenter指令時(shí)會(huì)嘗試取得lock對(duì)應(yīng)的monitor的所有權(quán)(CAS設(shè)置對(duì)象頭)辜羊,取得后即獲取到鎖,執(zhí)行monitorexit指令時(shí)會(huì)釋放monitor的所有權(quán)即釋放鎖词顾;

一個(gè)完整的demo

為了深入學(xué)習(xí)wait()和notify()八秃,先用完整的demo程序來(lái)模擬場(chǎng)景吧,以下是源碼:

publicclassNotifyDemo{privatestaticvoidsleep(longsleepVal){try{? ? ? ? ? ? Thread.sleep(sleepVal);? ? ? ? }catch(Exception e){? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }privatestaticvoidlog(String desc){? ? ? ? System.out.println(Thread.currentThread().getName() +" : "+ desc);? ? }? ? Object lock =newObject();publicvoidstartThreadA(){newThread(() -> {synchronized(lock){? ? ? ? ? ? ? ? log("get lock");? ? ? ? ? ? ? ? startThreadB();? ? ? ? ? ? ? ? log("start wait");try{? ? ? ? ? ? ? ? ? ? lock.wait();? ? ? ? ? ? ? ? }catch(InterruptedException e){? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? log("get lock after wait");? ? ? ? ? ? ? ? log("release lock");? ? ? ? ? ? }? ? ? ? },"thread-A").start();? ? }publicvoidstartThreadB(){newThread(()->{synchronized(lock){? ? ? ? ? ? ? ? log("get lock");? ? ? ? ? ? ? ? startThreadC();? ? ? ? ? ? ? ? sleep(100);? ? ? ? ? ? ? ? log("start notify");? ? ? ? ? ? ? ? lock.notify();? ? ? ? ? ? ? ? log("release lock");? ? ? ? ? ? }? ? ? ? },"thread-B").start();? ? }publicvoidstartThreadC(){newThread(() -> {synchronized(lock){? ? ? ? ? ? ? ? log("get lock");? ? ? ? ? ? ? ? log("release lock");? ? ? ? ? ? }? ? ? ? },"thread-C").start();? ? }publicstaticvoidmain(String[] args){newNotifyDemo().startThreadA();? ? }}


以上就是本次實(shí)戰(zhàn)用到的demo肉盹,代碼功能簡(jiǎn)述如下:

啟動(dòng)線程A昔驱,取得鎖之后先啟動(dòng)線程B再執(zhí)行wait()方法,釋放鎖并等待垮媒;

線程B啟動(dòng)之后會(huì)等待鎖舍悯,A線程執(zhí)行wait()之后,線程B取得鎖睡雇,然后啟動(dòng)線程C萌衬,再執(zhí)行notify喚醒線程A,最后退出synchronize代碼塊它抱,釋放鎖;

線程C啟動(dòng)之后就一直在等待鎖秕豫,這時(shí)候線程B還沒(méi)有退出synchronize代碼塊,鎖還在線程B手里;

線程A在線程B執(zhí)行notify()之后就一直在等待鎖混移,這時(shí)候線程B還沒(méi)有退出synchronize代碼塊祠墅,鎖還在線程B手里;

線程B退出synchronize代碼塊歌径,釋放鎖之后毁嗦,線程A和線程C競(jìng)爭(zhēng)鎖;

把上面的代碼在Openjdk8下面執(zhí)行回铛,反復(fù)執(zhí)行多次狗准,都得到以下結(jié)果:

thread-A : get lock

thread-A : start wait

thread-B : get lock

thread-C : c thread is start

thread-B : start notify

thread-B : release lock

thread-A : after wait, acquire lock again

thread-A : release lock

thread-C : get lock

thread-C : release lock

1

2

3

4

5

6

7

8

9

10

針對(duì)以上結(jié)果,問(wèn)題來(lái)了:?

第一個(gè)問(wèn)題:?

將以上代碼反復(fù)執(zhí)行多次茵肃,結(jié)果都是B釋放鎖之后A會(huì)先得到鎖腔长,這又是為什么呢?C為何不能先拿到鎖呢验残?

第二個(gè)問(wèn)題:?

線程C自開(kāi)始就執(zhí)行了monitorenter指令捞附,它能得到鎖是容易理解的,但是線程A呢您没?在wait()之后并沒(méi)有沒(méi)有monitorenter指令鸟召,那么它又是如何取得鎖的呢?

wait()紊婉、notify()這些方法都是native方法药版,所以只有從JVM源碼尋找答案了,本次閱讀的是openjdk8的源碼喻犁;

帶上問(wèn)題去看JVM源碼

按照demo代碼執(zhí)行順序槽片,我整理了如下問(wèn)題,帶著這些問(wèn)題去看JVM源碼可以聚焦主線肢础,不要被一些支線的次要的代碼卡住(例如一些異常處理还栓,監(jiān)控和上報(bào)等):?

1. 線程A在wait()的時(shí)候做了什么??

2. 線程C啟動(dòng)后传轰,由于此時(shí)線程B持有鎖剩盒,那么線程C此時(shí)在干啥??

3. 線程B在notify()的時(shí)候做了什么慨蛙??

4. 線程B釋放鎖的時(shí)候做了什么辽聊?

源碼中最重要的注釋信息

在源碼中有段注釋堪稱是整篇文章最重要的說(shuō)明,請(qǐng)大家始終記住這段信息期贫,處處都用得上:

ObjectWaiter對(duì)象存在于WaitSet跟匆、EntryList、cxq等集合中通砍,或者正在這些集合中移動(dòng)

原文如下:

請(qǐng)務(wù)必記住這三個(gè)集合:WaitSet玛臂、EntryList烤蜕、cxq

好了,接下來(lái)看源碼分析問(wèn)題吧:

線程A在wait()的時(shí)候做了什么

打開(kāi)hotspot/src/share/vm/runtime/objectMonitor.cpp,看ObjectMonitor::wait方法:

如上圖所示迹冤,有兩處代碼值得我們注意:?

1. 綠框中將當(dāng)前線程包裝成ObjectWaiter對(duì)象讽营,并且狀態(tài)為T(mén)S_WAIT,這里對(duì)應(yīng)的是jstack看到的線程狀態(tài)WAITING泡徙;?

2. 紅框中調(diào)用了AddWaiter方法橱鹏,跟進(jìn)去看下:

這個(gè)ObjectWaiter對(duì)象被放入了_WaitSet中,_WaitSet是個(gè)環(huán)形雙向鏈表(circular doubly linked list)

回到ObjectMonitor::wait方法接著往下看堪藐,會(huì)發(fā)現(xiàn)關(guān)鍵代碼如下圖蚀瘸,當(dāng)前線程通過(guò)park()方法開(kāi)始掛起(suspend):

至此,我們把wait()方法要做的事情就理清了:?

1. 包裝成ObjectWaiter對(duì)象庶橱,狀態(tài)為T(mén)S_WAIT贪惹;?

2. ObjectWaiter對(duì)象被放入_WaitSet中苏章;?

3. 當(dāng)前線程掛起;

線程B持有鎖的時(shí)候線程C在干啥

此時(shí)的線程C無(wú)法進(jìn)入synchronized{}代碼塊奏瞬,用jstack看應(yīng)該是BLOCKED狀態(tài)枫绅,如下圖:

我們看看monitorenter指令對(duì)應(yīng)的源碼吧,位置:openjdk/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread*thread, BasicObjectLock*elem))#ifdefASSERTthread->last_frame().interpreter_frame_verify_monitor(elem);#endifif(PrintBiasedLockingStatistics) {? ? Atomic::inc(BiasedLocking::slow_path_entry_count_addr());? }Handleh_obj(thread, elem->obj());? assert(Universe::heap()->is_in_reserved_or_null(h_obj()),"must be NULL or an object");if(UseBiasedLocking) {// Retry fast entry if bias is revoked to avoid unnecessary inflationObjectSynchronizer::fast_enter(h_obj, elem->lock(),true, CHECK);? }else{? ? ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);? }? assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),"must be NULL or an object");#ifdefASSERTthread->last_frame().interpreter_frame_verify_monitor(elem);#endifIRT_END

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

上面的代碼有個(gè)if (UseBiasedLocking)判斷硼端,是判斷是否使用偏向鎖的并淋,本例中的鎖顯然已經(jīng)不屬于當(dāng)前線程C了,所以我們還是直接看slow_enter(h_obj, elem->lock(), CHECK)方法吧珍昨;

打開(kāi)openjdk/hotspot/src/share/vm/runtime/synchronizer.cpp:

voidObjectSynchronizer::slow_enter(Handleobj, BasicLock*lock, TRAPS) {? markOop mark=obj->mark();? assert(!mark->has_bias_pattern(),"should not see bias pattern here");//是否處于無(wú)鎖狀態(tài)if(mark->is_neutral()) {// Anticipate successful CAS -- the ST of the displaced mark must// be visible <= the ST performed by the CAS.lock->set_displaced_header(mark);//無(wú)鎖狀態(tài)就去競(jìng)爭(zhēng)鎖if(mark==(markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {? ? ? TEVENT (slow_enter: release stacklock) ;return;? ? }// Fall through to inflate() ...}elseif(mark->has_locker()&&THREAD->is_lock_owned((address)mark->locker())) {//如果處于有鎖狀態(tài)县耽,就檢查是不是當(dāng)前線程持有鎖,如果是當(dāng)前線程持有的镣典,就return兔毙,然后就能執(zhí)行同步代碼塊中的代碼了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;? }#if0// The following optimization isn't particularly useful.if(mark->has_monitor()&&mark->monitor()->is_entered(THREAD)) {? ? lock->set_displaced_header (NULL) ;return;? }#endif// The object header will never be displaced to this lock,// so it does not matter what the value is, except that it// must be non-zero to avoid looking like a re-entrant lock,// and must not look locked either.lock->set_displaced_header(markOopDesc::unused_mark());//鎖膨脹ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

線程C在上面代碼中的執(zhí)行順序如下:?

1. 判斷是否是無(wú)鎖狀態(tài),如果是就通過(guò)Atomic::cmpxchg_ptr去競(jìng)爭(zhēng)鎖兄春;?

2. 不是無(wú)鎖狀態(tài)澎剥,就檢查當(dāng)前鎖是否是線程C持有;?

3. 不是線程C持有赶舆,調(diào)用inflate方法開(kāi)始鎖膨脹哑姚;

ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);

來(lái)看看鎖膨脹的源碼:

如上圖,鎖膨脹的代碼太長(zhǎng)芜茵,我們這里只看關(guān)鍵代碼吧:?

紅框中叙量,如果當(dāng)前狀態(tài)已經(jīng)是重量級(jí)鎖,就通過(guò)mark->monitor()方法取得ObjectMonitor指針再返回夕晓;?

綠框中宛乃,如果還不是重量級(jí)鎖,就檢查是否處于膨脹中狀態(tài)(其他線程正在膨脹中),如果是膨脹中征炼,就調(diào)用ReadStableMark方法進(jìn)行等待析既,ReadStableMark方法執(zhí)行完畢后再通過(guò)continue繼續(xù)檢查,ReadStableMark方法中還會(huì)調(diào)用os::NakedYield()釋放CPU資源谆奥;

如果紅框和綠框的條件都沒(méi)有命中眼坏,目前已經(jīng)是輕量級(jí)鎖了(不是重量級(jí)鎖并且不處于鎖膨脹狀態(tài)),可以開(kāi)始膨脹了酸些,如下圖:

簡(jiǎn)單來(lái)說(shuō)宰译,鎖膨脹就是通過(guò)CAS將監(jiān)視器對(duì)象OjectMonitor的狀態(tài)設(shè)置為INFLATING,如果CAS失敗魄懂,就在此循環(huán)沿侈,再走前一副圖中的的紅框和綠框中的判斷,如果CAS設(shè)置成功市栗,會(huì)繼續(xù)設(shè)置ObjectMonitor中的header缀拭、owner等字段,然后inflate方法返回監(jiān)視器對(duì)象OjectMonitor填帽;

看看之前slow_enter方法中蛛淋,調(diào)用inflate方法的代碼如下:

ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);

1

所以inflate方法返回監(jiān)視器對(duì)象OjectMonitor之后,會(huì)立刻執(zhí)行OjectMonitor的enter方法篡腌,這個(gè)方法中開(kāi)始競(jìng)爭(zhēng)鎖了褐荷,方法在openjdk/hotspot/src/share/vm/runtime/objectMonitor.cpp文件中:

如上圖,紅框中表示OjectMonitor的enter方法一進(jìn)來(lái)就通過(guò)CAS將OjectMonitor的_owner設(shè)置為當(dāng)前線程嘹悼,綠框中表示設(shè)置成功的邏輯叛甫,第一個(gè)if表示重入鎖的邏輯,第二個(gè)if表示第一次設(shè)置_owner成功绘迁,都意味著競(jìng)爭(zhēng)鎖成功合溺,而我們的線程C顯然是競(jìng)爭(zhēng)失敗的,會(huì)進(jìn)入下圖中的無(wú)線循環(huán)缀台,反復(fù)調(diào)用EnterI方法:

進(jìn)入EnterI方法看看:

如上圖棠赛,首先構(gòu)造一個(gè)ObjectWaiter對(duì)象node,后面的for(;;)代碼塊中來(lái)是一段非常巧妙的代碼膛腐,同一時(shí)刻可能有多個(gè)線程都競(jìng)爭(zhēng)鎖失敗走進(jìn)這個(gè)EnterI方法睛约,所以在這個(gè)for循環(huán)中,用CAS將_cxq地址放入node的_next哲身,也就是把node放到_cxq隊(duì)列的首位辩涝,如果CAS失敗,就表示其他線程把node放入到_cxq的首位了勘天,所以通過(guò)for循環(huán)再放一次怔揩,只要成功捉邢,此node就一定在最新的_cxq隊(duì)列的首位。

接下來(lái)的代碼又是一個(gè)無(wú)限循環(huán)商膊,如下圖:

從上圖可以看出伏伐,進(jìn)入循環(huán)后先調(diào)用TryLock方法競(jìng)爭(zhēng)一次鎖,如果成功了就退出循環(huán)晕拆,否則就調(diào)用Self->_ParkEvent->park方法使線程掛起岛杀,這里有自旋鎖的邏輯速妖,也就是park方法帶了時(shí)間參數(shù),就會(huì)在掛起一段時(shí)間后自動(dòng)喚醒兼贸,如果不是自旋的條件弧圆,就一直掛起等待被其他條件喚醒谨胞,線程被喚醒后又會(huì)執(zhí)行TryLock方法競(jìng)爭(zhēng)一次鎖榄棵,競(jìng)爭(zhēng)不到繼續(xù)這個(gè)for循環(huán)揩晴;

到這里我們已經(jīng)把線程C在BLOCK的時(shí)候的邏輯理清楚了,小結(jié)如下:

偏向鎖邏輯整吆,未命中未舟;

如果是無(wú)鎖狀態(tài),就通過(guò)CAS去競(jìng)爭(zhēng)鎖掂为,此處由于鎖已經(jīng)被線程B持有,所以不是無(wú)鎖狀態(tài)员串;

不是無(wú)鎖狀態(tài)勇哗,而且鎖不是線程C持有,執(zhí)行鎖膨脹寸齐,構(gòu)造OjectMonitor對(duì)象欲诺;

競(jìng)爭(zhēng)鎖,競(jìng)爭(zhēng)失敗就將線程加入_cxq隊(duì)列的首位渺鹦;

開(kāi)始無(wú)限循環(huán)扰法,競(jìng)爭(zhēng)鎖成功就退出循環(huán),競(jìng)爭(zhēng)失敗線程掛起毅厚,等待被喚醒后繼續(xù)競(jìng)爭(zhēng)塞颁;

線程B在notify()的時(shí)候做了什么

接下來(lái)該線程B執(zhí)行notify了,代碼是objectMonitor.cpp的ObjectMonitor::notify方法:

如上圖所示吸耿,首先是Policy的賦值祠锣,其次是調(diào)用DequeueWaiter()方法將_WaitSet隊(duì)列的第一個(gè)值取出并返回,還記得_WaitSet么咽安?所有wait的線程都被包裝成ObjectWaiter對(duì)象然后放進(jìn)來(lái)了伴网;?

接下來(lái)對(duì)ObjectWaiter對(duì)象的處理方式,根據(jù)Policy的不同而不同:?

Policy == 0:放入_EntryList隊(duì)列的排頭位置妆棒;?

Policy == 1:放入_EntryList隊(duì)列的末尾位置澡腾;?

Policy == 2:_EntryList隊(duì)列為空就放入_EntryList沸伏,否則放入_cxq隊(duì)列的排頭位置;

如上圖所示动分,請(qǐng)注意把ObjectWaiter的地址寫(xiě)到_cxq變量的時(shí)候要用CAS操作毅糟,因?yàn)榇藭r(shí)可能有其他線程正在競(jìng)爭(zhēng)鎖,競(jìng)爭(zhēng)失敗的時(shí)候會(huì)將自己包裝成ObjectWaiter對(duì)象加入到_cxq中刺啦;

這里的代碼有一處疑問(wèn)留特,期待著讀著您的指教:如果_EntryList為空,就把ObjectWaiter放入ObjectWaiter中玛瘸,為什么要這樣做呢蜕青?

Policy == 3:放入_cxq隊(duì)列中,末尾位置糊渊;更新_cxq變量的值的時(shí)候右核,同樣要通過(guò)CAS注意并發(fā)問(wèn)題;

這里有一段很巧妙的代碼渺绒,現(xiàn)將_cxq保存在Tail中贺喝,正常情況下將ObjectWaiter賦值給Tail->_next就可以了,但是此時(shí)有可能其他線程正在_cxq的尾部追加數(shù)據(jù)了宗兼,所以此時(shí)Tail對(duì)象對(duì)應(yīng)的記錄就不是最后一條了躏鱼,那么它的_next就非空了,一旦發(fā)生這種情況殷绍,就執(zhí)行Tail = Tail->_next染苛,這樣就獲得了最新的_cxq的尾部數(shù)據(jù),如下圖所示:

Policy等于其他值主到,立即喚醒ObjectWaiter對(duì)應(yīng)的線程茶行;

小結(jié)一下,線程B執(zhí)行notify時(shí)候做的事情:

執(zhí)行過(guò)wait的線程都在隊(duì)列_WaitSet中登钥,此處從_WaitSet中取出第一個(gè)畔师;

根據(jù)Policy的不同,將這個(gè)線程放入_EntryList或者_(dá)cxq隊(duì)列中的起始或末尾位置牧牢;

線程B釋放鎖的時(shí)候做了什么

接下來(lái)到了揭開(kāi)問(wèn)題的關(guān)鍵了看锉,我們來(lái)看objectMonitor.cpp的ObjectMonitor::exit方法;

如上圖塔鳍,方法一進(jìn)來(lái)先做一些合法性判斷度陆,接下來(lái)如紅框所示,是偏向鎖邏輯献幔,偏向次數(shù)減一后直接返回懂傀,顯然線程B在此處不會(huì)返回,而是繼續(xù)往下執(zhí)行蜡感;

根據(jù)QMode的不同蹬蚁,有不同的處理方式:?

1. QMode = 2恃泪,并且_cxq非空:取_cxq隊(duì)列排頭位置的ObjectWaiter對(duì)象,調(diào)用ExitEpilog方法犀斋,該方法會(huì)喚醒ObjectWaiter對(duì)象的線程贝乎,此處會(huì)立即返回,后面的代碼不會(huì)執(zhí)行了叽粹;?

2. QMode = 3览效,并且_cxq非空:把_cxq隊(duì)列首元素放入_EntryList的尾部;?

3. QMode = 4虫几,并且_cxq非空:把_cxq隊(duì)列首元素放入_EntryList的頭部锤灿;?

4. QMode = 0,不做什么辆脸,繼續(xù)往下看但校;

只有QMode=2的時(shí)候會(huì)提前返回,等于0啡氢、3状囱、4的時(shí)候都會(huì)繼續(xù)往下執(zhí)行:

如果_EntryList的首元素非空,就取出來(lái)調(diào)用ExitEpilog方法倘是,該方法會(huì)喚醒ObjectWaiter對(duì)象的線程亭枷,然后立即返回;?

如果_EntryList的首元素為空搀崭,就取_cxq的首元素奶栖,放入_EntryList,然后再?gòu)腳EntryList中取出來(lái)執(zhí)行ExitEpilog方法门坷,然后立即返回;

以上操作袍镀,均是執(zhí)行過(guò)ExitEpilog方法然后立即返回默蚌,如果取出的元素為空,就執(zhí)行循環(huán)繼續(xù)任邸绸吸;

小結(jié)一下,線程B釋放了鎖之后设江,執(zhí)行的操作如下:?

1. 偏向鎖邏輯锦茁,此處未命中;?

2. 根據(jù)QMode的不同叉存,將ObjectWaiter從_cxq或者_(dá)EntryList中取出后喚醒码俩;?

3. 喚醒的元素會(huì)繼續(xù)執(zhí)行掛起前的代碼,按照我們之前的分析歼捏,線程喚醒后稿存,就會(huì)通過(guò)CAS去競(jìng)爭(zhēng)鎖笨篷,此時(shí)由于線程B已經(jīng)釋放了鎖,那么此時(shí)應(yīng)該能競(jìng)爭(zhēng)成功瓣履;

到了現(xiàn)在已經(jīng)將之前的幾個(gè)問(wèn)題搞清了率翅,匯總起來(lái)看看:?

1. 線程A在wait() 后被加入了_WaitSet隊(duì)列中;?

2. 線程C被線程B啟動(dòng)后競(jìng)爭(zhēng)鎖失敗袖迎,被加入到_cxq隊(duì)列的首位冕臭;?

3. 線程B在notify()時(shí),從_WaitSet中取出第一個(gè)燕锥,根據(jù)Policy的不同辜贵,將這個(gè)線程放入_EntryList或者_(dá)cxq隊(duì)列中的起始或末尾位置;?

4. 根據(jù)QMode的不同脯宿,將ObjectWaiter從_cxq或者_(dá)EntryList中取出后喚醒念颈;;

所以连霉,最初的問(wèn)題已經(jīng)清楚了榴芳,wait()的線程被喚醒后,會(huì)進(jìn)入一個(gè)隊(duì)列跺撼,然后JVM會(huì)根據(jù)Policy和QMode的不同對(duì)隊(duì)列中的ObjectWaiter做不同的處理窟感,被選中的ObjectWaiter會(huì)被喚醒,去競(jìng)爭(zhēng)鎖歉井;

至此柿祈,源碼分析已結(jié)束,但是因?yàn)槲覀儾恢繮olicy和QMode參數(shù)到底是多少哩至,所以還不能對(duì)之前的問(wèn)題有個(gè)明確的結(jié)果躏嚎,這些還是留在下一章來(lái)解答吧,下一章里我們?nèi)バ薷腏VM源碼菩貌,把參數(shù)都打印出來(lái)卢佣;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市箭阶,隨后出現(xiàn)的幾起案子虚茶,更是在濱河造成了極大的恐慌,老刑警劉巖仇参,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘹叫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡诈乒,警方通過(guò)查閱死者的電腦和手機(jī)罩扇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)怕磨,“玉大人暮蹂,你說(shuō)我怎么就攤上這事寞缝。” “怎么了仰泻?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵荆陆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我集侯,道長(zhǎng)被啼,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任棠枉,我火速辦了婚禮浓体,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辈讶。我一直安慰自己命浴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布贱除。 她就那樣靜靜地躺著生闲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪月幌。 梳的紋絲不亂的頭發(fā)上碍讯,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音扯躺,去河邊找鬼捉兴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛录语,可吹牛的內(nèi)容都是我干的倍啥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼澎埠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼虽缕!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起失暂,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鳄虱,沒(méi)想到半個(gè)月后弟塞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拙已,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年决记,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倍踪。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡系宫,死狀恐怖索昂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扩借,我是刑警寧澤椒惨,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站潮罪,受9級(jí)特大地震影響康谆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嫉到,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一沃暗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧何恶,春花似錦孽锥、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至今艺,卻和暖如春韵丑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虚缎。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工撵彻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人实牡。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓陌僵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親创坞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子碗短,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • JAVA面試題 1、作用域public,private,protected,以及不寫(xiě)時(shí)的區(qū)別答:區(qū)別如下:作用域 ...
    JA尐白閱讀 1,143評(píng)論 1 0
  • 1.解決信號(hào)量丟失和假喚醒 public class MyWaitNotify3{ MonitorObject m...
    Q羅閱讀 871評(píng)論 0 1
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉题涨,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,679評(píng)論 0 9
  • 本文出自 Eddy Wiki 偎谁,轉(zhuǎn)載請(qǐng)注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 2,055評(píng)論 0 14
  • 1.產(chǎn)權(quán) 產(chǎn)權(quán)購(gòu)房者首先要弄清所購(gòu)房屋的產(chǎn)權(quán)歸屬。因產(chǎn)權(quán)歸屬不清楚或產(chǎn)權(quán)糾紛未了結(jié)的房屋纲堵,購(gòu)置時(shí)務(wù)必謹(jǐn)慎對(duì)待巡雨,較好...
    我家的小鯉魚(yú)閱讀 258評(píng)論 2 2