<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底層實(shí)現(xiàn)

          共 8637字,需瀏覽 18分鐘

           ·

          2021-02-09 12:29

          關(guān)于synchronized的底層實(shí)現(xiàn),網(wǎng)上有很多文章了。但是很多文章要么作者根本沒看代碼,僅僅是根據(jù)網(wǎng)上其他文章總結(jié)、照搬而成,難免有些錯(cuò)誤;要么很多點(diǎn)都是一筆帶過,對(duì)于為什么這樣實(shí)現(xiàn)沒有一個(gè)說法,讓像我這樣的讀者意猶未盡。

          本系列文章將對(duì)HotSpot的synchronized鎖實(shí)現(xiàn)進(jìn)行全面分析,內(nèi)容包括偏向鎖、輕量級(jí)鎖、重量級(jí)鎖的加鎖、解鎖、鎖升級(jí)流程的原理及源碼分析,希望給在研究synchronized路上的同學(xué)一些幫助。

          大概花費(fèi)了兩周的實(shí)現(xiàn)看代碼(花費(fèi)了這么久時(shí)間有些懺愧,主要是對(duì)C++、JVM底層機(jī)制、JVM調(diào)試以及匯編代碼不太熟),將synchronized涉及到的代碼基本都看了一遍,其中還包括在JVM中添加日志驗(yàn)證自己的猜想,總的來說目前對(duì)synchronized這塊有了一個(gè)比較全面清晰的認(rèn)識(shí),但水平有限,有些細(xì)節(jié)難免有些疏漏,還望請(qǐng)大家指正。

          本篇文章將對(duì)synchronized機(jī)制做個(gè)大致的介紹,包括用以承載鎖狀態(tài)的對(duì)象頭、鎖的幾種形式、各種形式鎖的加鎖和解鎖流程、什么時(shí)候會(huì)發(fā)生鎖升級(jí)。需要注意的是本文旨在介紹背景和概念,在講述一些流程的時(shí)候,只提到了主要case,對(duì)于實(shí)現(xiàn)細(xì)節(jié)、運(yùn)行時(shí)的不同分支都在后面的文章中詳細(xì)分析。

          本人看的JVM版本是jdk8u,具體版本號(hào)以及代碼可以在這里看到。

          http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683

          一、synchronized簡(jiǎn)介

          Java中提供了兩種實(shí)現(xiàn)同步的基礎(chǔ)語義:synchronized方法和synchronized塊, 我們來看個(gè)demo:

          1. public class SyncTest {

          2. ? ?public void syncBlock(){

          3. ? ? ? ?synchronized (this){

          4. ? ? ? ? ? ?System.out.println("hello block");

          5. ? ? ? ?}

          6. ? ?}

          7. ? ?public synchronized void syncMethod(){

          8. ? ? ? ?System.out.println("hello method");

          9. ? ?}

          10. }

          當(dāng)SyncTest.java被編譯成class文件的時(shí)候,synchronized關(guān)鍵字和synchronized方法的字節(jié)碼略有不同,我們可以用javap -v 命令查看class文件對(duì)應(yīng)的JVM字節(jié)碼信息,部分信息如下:

          1. {

          2. ?public void syncBlock();

          3. ? ?descriptor: ()V

          4. ? ?flags: ACC_PUBLIC

          5. ? ?Code:

          6. ? ? ?stack=2, locals=3, args_size=1

          7. ? ? ? ? 0: aload_0

          8. ? ? ? ? 1: dup

          9. ? ? ? ? 2: astore_1

          10. ? ? ? ? 3: monitorenter ? ? ? ? ? ? ? ? ? ? ?// monitorenter指令進(jìn)入同步塊

          11. ? ? ? ? 4: getstatic ? ? #2 ? ? ? ? ? ? ? ? ?// Field java/lang/System.out:Ljava/io/PrintStream;

          12. ? ? ? ? 7: ldc ? ? ? ? ? #3 ? ? ? ? ? ? ? ? ?// String hello block

          13. ? ? ? ? 9: invokevirtual #4 ? ? ? ? ? ? ? ? ?// Method java/io/PrintStream.println:(Ljava/lang/String;)V

          14. ? ? ? ?12: aload_1

          15. ? ? ? ?13: monitorexit ? ? ? ? ? ? ? ? ? ? ? ? ?// monitorexit指令退出同步塊

          16. ? ? ? ?14: goto ? ? ? ? ?22

          17. ? ? ? ?17: astore_2

          18. ? ? ? ?18: aload_1

          19. ? ? ? ?19: monitorexit ? ? ? ? ? ? ? ? ? ? ? ? ?// monitorexit指令退出同步塊

          20. ? ? ? ?20: aload_2

          21. ? ? ? ?21: athrow

          22. ? ? ? ?22: return

          23. ? ? ?Exception table:

          24. ? ? ? ? from ? ?to ?target type

          25. ? ? ? ? ? ? 4 ? ?14 ? ?17 ? any

          26. ? ? ? ? ? ?17 ? ?20 ? ?17 ? any



          27. ?public synchronized void syncMethod();

          28. ? ?descriptor: ()V

          29. ? ?flags: ACC_PUBLIC, ACC_SYNCHRONIZED ? ? ?//添加了ACC_SYNCHRONIZED標(biāo)記

          30. ? ?Code:

          31. ? ? ?stack=2, locals=1, args_size=1

          32. ? ? ? ? 0: getstatic ? ? #2 ? ? ? ? ? ? ? ? ?// Field java/lang/System.out:Ljava/io/PrintStream;

          33. ? ? ? ? 3: ldc ? ? ? ? ? #5 ? ? ? ? ? ? ? ? ?// String hello method

          34. ? ? ? ? 5: invokevirtual #4 ? ? ? ? ? ? ? ? ?// Method java/io/PrintStream.println:(Ljava/lang/String;)V

          35. ? ? ? ? 8: return


          36. }

          從上面的中文注釋處可以看到,對(duì)于synchronized關(guān)鍵字而言,javac在編譯時(shí),會(huì)生成對(duì)應(yīng)的monitorenter和monitorexit指令分別對(duì)應(yīng)synchronized同步塊的進(jìn)入和退出,有兩個(gè)monitorexit指令的原因是:為了保證拋異常的情況下也能釋放鎖,所以javac為同步代碼塊添加了一個(gè)隱式的try-finally,在finally中會(huì)調(diào)用monitorexit命令釋放鎖。而對(duì)于synchronized方法而言,javac為其生成了一個(gè)ACCSYNCHRONIZED關(guān)鍵字,在JVM進(jìn)行方法調(diào)用時(shí),發(fā)現(xiàn)調(diào)用的方法被ACCSYNCHRONIZED修飾,則會(huì)先嘗試獲得鎖。

          在JVM底層,對(duì)于這兩種synchronized語義的實(shí)現(xiàn)大致相同,在后文中會(huì)選擇一種進(jìn)行詳細(xì)分析。

          因?yàn)楸疚闹荚诜治鰏ynchronized的實(shí)現(xiàn)原理,因此對(duì)于其使用的一些問題就不贅述了,不了解的朋友可以看看這篇文章

          https://blog.csdn.net/luoweifu/article/details/46613015

          二、鎖的幾種形式

          傳統(tǒng)的鎖(也就是下文要說的重量級(jí)鎖)依賴于系統(tǒng)的同步函數(shù),在linux上使用mutex互斥鎖,最底層實(shí)現(xiàn)依賴于futex,關(guān)于futex可以看這些文章,這些同步函數(shù)都涉及到用戶態(tài)和內(nèi)核態(tài)的切換、進(jìn)程的上下文切換,成本較高。對(duì)于加了synchronized關(guān)鍵字但運(yùn)行時(shí)并沒有多線程競(jìng)爭(zhēng),或兩個(gè)線程接近于交替執(zhí)行的情況,使用傳統(tǒng)鎖機(jī)制無疑效率是會(huì)比較低的。

          https://github.com/farmerjohngit/myblog/issues/8

          在JDK 1.6之前,synchronized只有傳統(tǒng)的鎖機(jī)制,因此給開發(fā)者留下了synchronized關(guān)鍵字相比于其他同步機(jī)制性能不好的印象。

          在JDK 1.6引入了兩種新型鎖機(jī)制:偏向鎖和輕量級(jí)鎖,它們的引入是為了解決在沒有多線程競(jìng)爭(zhēng)或基本沒有競(jìng)爭(zhēng)的場(chǎng)景下因使用傳統(tǒng)鎖機(jī)制帶來的性能開銷問題。

          在看這幾種鎖機(jī)制的實(shí)現(xiàn)前,我們先來了解下對(duì)象頭,它是實(shí)現(xiàn)多種鎖機(jī)制的基礎(chǔ)。

          1.對(duì)象頭

          因?yàn)樵贘ava中任意對(duì)象都可以用作鎖,因此必定要有一個(gè)映射關(guān)系,存儲(chǔ)該對(duì)象以及其對(duì)應(yīng)的鎖信息(比如當(dāng)前哪個(gè)線程持有鎖,哪些線程在等待)。一種很直觀的方法是,用一個(gè)全局map,來存儲(chǔ)這個(gè)映射關(guān)系,但這樣會(huì)有一些問題:需要對(duì)map做線程安全保障,不同的synchronized之間會(huì)相互影響,性能差;另外當(dāng)同步對(duì)象較多時(shí),該map可能會(huì)占用比較多的內(nèi)存。

          所以最好的辦法是將這個(gè)映射關(guān)系存儲(chǔ)在對(duì)象頭中,因?yàn)閷?duì)象頭本身也有一些hashcode、GC相關(guān)的數(shù)據(jù),所以如果能將鎖信息與這些信息共存在對(duì)象頭中就好了。

          在JVM中,對(duì)象在內(nèi)存中除了本身的數(shù)據(jù)外還會(huì)有個(gè)對(duì)象頭,對(duì)于普通對(duì)象而言,其對(duì)象頭中有兩類信息:mark word和類型指針。另外對(duì)于數(shù)組而言還會(huì)有一份記錄數(shù)組長(zhǎng)度的數(shù)據(jù)。

          類型指針是指向該對(duì)象所屬類對(duì)象的指針,mark word用于存儲(chǔ)對(duì)象的HashCode、GC分代年齡、鎖狀態(tài)等信息。在32位系統(tǒng)上mark word長(zhǎng)度為32字節(jié),64位系統(tǒng)上長(zhǎng)度為64字節(jié)。為了能在有限的空間里存儲(chǔ)下更多的數(shù)據(jù),其存儲(chǔ)格式是不固定的,在32位系統(tǒng)上各狀態(tài)的格式如下:

          可以看到鎖信息也是存在于對(duì)象的mark word中的。當(dāng)對(duì)象狀態(tài)為偏向鎖(biasable)時(shí),mark word存儲(chǔ)的是偏向的線程ID;當(dāng)狀態(tài)為輕量級(jí)鎖(lightweight locked)時(shí),mark word存儲(chǔ)的是指向線程棧中Lock Record的指針;當(dāng)狀態(tài)為重量級(jí)鎖(inflated)時(shí),為指向堆中的monitor對(duì)象的指針。

          2.重量級(jí)鎖

          重量級(jí)鎖是我們常說的傳統(tǒng)意義上的鎖,其利用操作系統(tǒng)底層的同步機(jī)制去實(shí)現(xiàn)Java中的線程同步。

          重量級(jí)鎖的狀態(tài)下,對(duì)象的mark word為指向一個(gè)堆中monitor對(duì)象的指針。

          一個(gè)monitor對(duì)象包括這么幾個(gè)關(guān)鍵字段:cxq(下圖中的ContentionList),EntryList ,WaitSet,owner。

          其中cxq ,EntryList ,WaitSet都是由ObjectWaiter的鏈表結(jié)構(gòu),owner指向持有鎖的線程。?

          當(dāng)一個(gè)線程嘗試獲得鎖時(shí),如果該鎖已經(jīng)被占用,則會(huì)將該線程封裝成一個(gè)ObjectWaiter對(duì)象插入到cxq的隊(duì)列尾部,然后暫停當(dāng)前線程。當(dāng)持有鎖的線程釋放鎖前,會(huì)將cxq中的所有元素移動(dòng)到EntryList中去,并喚醒EntryList的隊(duì)首線程。

          如果一個(gè)線程在同步塊中調(diào)用了Object#wait方法,會(huì)將該線程對(duì)應(yīng)的ObjectWaiter從EntryList移除并加入到WaitSet中,然后釋放鎖。當(dāng)wait的線程被notify之后,會(huì)將對(duì)應(yīng)的ObjectWaiter從WaitSet移動(dòng)到EntryList中。

          以上只是對(duì)重量級(jí)鎖流程的一個(gè)簡(jiǎn)述,其中涉及到的很多細(xì)節(jié),比如ObjectMonitor對(duì)象從哪來?釋放鎖時(shí)是將cxq中的元素移動(dòng)到EntryList的尾部還是頭部?notfiy時(shí),是將ObjectWaiter移動(dòng)到EntryList的尾部還是頭部?

          關(guān)于具體的細(xì)節(jié),會(huì)在重量級(jí)鎖的文章中分析。

          3.輕量級(jí)鎖

          JVM的開發(fā)者發(fā)現(xiàn)在很多情況下,在Java程序運(yùn)行時(shí),同步塊中的代碼都是不存在競(jìng)爭(zhēng)的,不同的線程交替的執(zhí)行同步塊中的代碼。這種情況下,用重量級(jí)鎖是沒必要的。因此JVM引入了輕量級(jí)鎖的概念。

          線程在執(zhí)行同步塊之前,JVM會(huì)先在當(dāng)前的線程的棧幀中創(chuàng)建一個(gè)Lock Record,其包括一個(gè)用于存儲(chǔ)對(duì)象頭中的 mark word(官方稱之為Displaced Mark Word)以及一個(gè)指向?qū)ο蟮闹羔槨O聢D右邊的部分就是一個(gè)Lock Record。

          加鎖過程:

          1.在線程棧中創(chuàng)建一個(gè)Lock Record,將其obj(即上圖的Object reference)字段指向鎖對(duì)象。

          2.直接通過CAS指令將Lock Record的地址存儲(chǔ)在對(duì)象頭的mark word中,如果對(duì)象處于無鎖狀態(tài)則修改成功,代表該線程獲得了輕量級(jí)鎖。如果失敗,進(jìn)入到步驟3。

          3.如果是當(dāng)前線程已經(jīng)持有該鎖了,代表這是一次鎖重入。設(shè)置Lock Record第一部分(Displaced Mark Word)為null,起到了一個(gè)重入計(jì)數(shù)器的作用。然后結(jié)束。

          4.走到這一步說明發(fā)生了競(jìng)爭(zhēng),需要膨脹為重量級(jí)鎖。

          解鎖過程:

          1.遍歷線程棧,找到所有obj字段等于當(dāng)前鎖對(duì)象的Lock Record。

          2.如果Lock Record的Displaced Mark Word為null,代表這是一次重入,將obj設(shè)置為null后continue。

          3.如果Lock Record的Displaced Mark Word不為null,則利用CAS指令將對(duì)象頭的mark word恢復(fù)成為Displaced Mark Word。如果成功,則continue,否則膨脹為重量級(jí)鎖。

          4.偏向鎖

          Java是支持多線程的語言,因此在很多二方包、基礎(chǔ)庫中為了保證代碼在多線程的情況下也能正常運(yùn)行,也就是我們常說的線程安全,都會(huì)加入如synchronized這樣的同步語義。但是在應(yīng)用在實(shí)際運(yùn)行時(shí),很可能只有一個(gè)線程會(huì)調(diào)用相關(guān)同步方法。比如下面這個(gè)demo:

          1. import java.util.ArrayList;

          2. import java.util.List;


          3. public class SyncDemo1 {


          4. ? ?public static void main(String[] args) {

          5. ? ? ? ?SyncDemo1 syncDemo1 = new SyncDemo1();

          6. ? ? ? ?for (int i = 0; i < 100; i++) {

          7. ? ? ? ? ? ?syncDemo1.addString("test:" + i);

          8. ? ? ? ?}

          9. ? ?}


          10. ? ?private List<String> list = new ArrayList<>();


          11. ? ?public synchronized void addString(String s) {

          12. ? ? ? ?list.add(s);

          13. ? ?}


          14. }

          在這個(gè)demo中為了保證對(duì)list操縱時(shí)線程安全,對(duì)addString方法加了synchronized的修飾,但實(shí)際使用時(shí)卻只有一個(gè)線程調(diào)用到該方法,對(duì)于輕量級(jí)鎖而言,每次調(diào)用addString時(shí),加鎖解鎖都有一個(gè)CAS操作;對(duì)于重量級(jí)鎖而言,加鎖也會(huì)有一個(gè)或多個(gè)CAS操作(這里的’一個(gè)‘、’多個(gè)‘?dāng)?shù)量詞只是針對(duì)該demo,并不適用于所有場(chǎng)景)。

          在JDK1.6中為了提高一個(gè)對(duì)象在一段很長(zhǎng)的時(shí)間內(nèi)都只被一個(gè)線程用做鎖對(duì)象場(chǎng)景下的性能,引入了偏向鎖,在第一次獲得鎖時(shí),會(huì)有一個(gè)CAS操作,之后該線程再獲取鎖,只會(huì)執(zhí)行幾個(gè)簡(jiǎn)單的命令,而不是開銷相對(duì)較大的CAS命令。我們來看看偏向鎖是如何做的。

          對(duì)象創(chuàng)建

          當(dāng)JVM啟用了偏向鎖模式(1.6以上默認(rèn)開啟),當(dāng)新創(chuàng)建一個(gè)對(duì)象的時(shí)候,如果該對(duì)象所屬的class沒有關(guān)閉偏向鎖模式(什么時(shí)候會(huì)關(guān)閉一個(gè)class的偏向模式下文會(huì)說,默認(rèn)所有class的偏向模式都是是開啟的),那新創(chuàng)建對(duì)象的mark word將是可偏向狀態(tài),此時(shí)mark word中的thread id(參見上文偏向狀態(tài)下的mark word格式)為0,表示未偏向任何線程,也叫做匿名偏向(anonymously biased)。

          加鎖過程

          case 1:當(dāng)該對(duì)象第一次被線程獲得鎖的時(shí)候,發(fā)現(xiàn)是匿名偏向狀態(tài),則會(huì)用CAS指令,將mark word中的thread id由0改成當(dāng)前線程Id。如果成功,則代表獲得了偏向鎖,繼續(xù)執(zhí)行同步塊中的代碼。否則,將偏向鎖撤銷,升級(jí)為輕量級(jí)鎖。

          case 2:當(dāng)被偏向的線程再次進(jìn)入同步塊時(shí),發(fā)現(xiàn)鎖對(duì)象偏向的就是當(dāng)前線程,在通過一些額外的檢查后(細(xì)節(jié)見后面的文章),會(huì)往當(dāng)前線程的棧中添加一條Displaced Mark Word為空的Lock Record中,然后繼續(xù)執(zhí)行同步塊的代碼,因?yàn)椴倏v的是線程私有的棧,因此不需要用到CAS指令;由此可見偏向鎖模式下,當(dāng)被偏向的線程再次嘗試獲得鎖時(shí),僅僅進(jìn)行幾個(gè)簡(jiǎn)單的操作就可以了,在這種情況下,synchronized關(guān)鍵字帶來的性能開銷基本可以忽略。

          case 3.當(dāng)其他線程進(jìn)入同步塊時(shí),發(fā)現(xiàn)已經(jīng)有偏向的線程了,則會(huì)進(jìn)入到撤銷偏向鎖的邏輯里,一般來說,會(huì)在safepoint中去查看偏向的線程是否還存活,如果存活且還在同步塊中則將鎖升級(jí)為輕量級(jí)鎖,原偏向的線程繼續(xù)擁有鎖,當(dāng)前線程則走入到鎖升級(jí)的邏輯里;如果偏向的線程已經(jīng)不存活或者不在同步塊中,則將對(duì)象頭的mark word改為無鎖狀態(tài)(unlocked),之后再升級(jí)為輕量級(jí)鎖。

          由此可見,偏向鎖升級(jí)的時(shí)機(jī)為:當(dāng)鎖已經(jīng)發(fā)生偏向后,只要有另一個(gè)線程嘗試獲得偏向鎖,則該偏向鎖就會(huì)升級(jí)成輕量級(jí)鎖。當(dāng)然這個(gè)說法不絕對(duì),因?yàn)檫€有批量重偏向這一機(jī)制。

          解鎖過程

          當(dāng)有其他線程嘗試獲得鎖時(shí),是根據(jù)遍歷偏向線程的lock record來確定該線程是否還在執(zhí)行同步塊中的代碼。因此偏向鎖的解鎖很簡(jiǎn)單,僅僅將棧中的最近一條lock record的obj字段設(shè)置為null。需要注意的是,偏向鎖的解鎖步驟中并不會(huì)修改對(duì)象頭中的thread id。

          下圖展示了鎖狀態(tài)的轉(zhuǎn)換流程:?

          另外,偏向鎖默認(rèn)不是立即就啟動(dòng)的,在程序啟動(dòng)后,通常有幾秒的延遲,可以通過命令 -XX:BiasedLockingStartupDelay=0來關(guān)閉延遲。

          批量重偏向與撤銷

          從上文偏向鎖的加鎖解鎖過程中可以看出,當(dāng)只有一個(gè)線程反復(fù)進(jìn)入同步塊時(shí),偏向鎖帶來的性能開銷基本可以忽略,但是當(dāng)有其他線程嘗試獲得鎖時(shí),就需要等到safe point時(shí)將偏向鎖撤銷為無鎖狀態(tài)或升級(jí)為輕量級(jí)/重量級(jí)鎖。safe point這個(gè)詞我們?cè)贕C中經(jīng)常會(huì)提到,其代表了一個(gè)狀態(tài),在該狀態(tài)下所有線程都是暫停的(大概這么個(gè)意思),詳細(xì)可以看這篇文章。總之,偏向鎖的撤銷是有一定成本的,如果說運(yùn)行時(shí)的場(chǎng)景本身存在多線程競(jìng)爭(zhēng)的,那偏向鎖的存在不僅不能提高性能,而且會(huì)導(dǎo)致性能下降。因此,JVM中增加了一種批量重偏向/撤銷的機(jī)制。

          https://blog.csdn.net/ITer_ZC/article/details/41892567

          存在如下兩種情況:(見官方論文第4小節(jié)):

          https://www.oracle.com/technetwork/java/biasedlocking-oopsla2006-wp-149958.pdf

          1.一個(gè)線程創(chuàng)建了大量對(duì)象并執(zhí)行了初始的同步操作,之后在另一個(gè)線程中將這些對(duì)象作為鎖進(jìn)行之后的操作。這種case下,會(huì)導(dǎo)致大量的偏向鎖撤銷操作。

          2.存在明顯多線程競(jìng)爭(zhēng)的場(chǎng)景下使用偏向鎖是不合適的,例如生產(chǎn)者/消費(fèi)者隊(duì)列。

          批量重偏向(bulk rebias)機(jī)制是為了解決第一種場(chǎng)景。批量撤銷(bulk revoke)則是為了解決第二種場(chǎng)景。

          其做法是:以class為單位,為每個(gè)class維護(hù)一個(gè)偏向鎖撤銷計(jì)數(shù)器,每一次該class的對(duì)象發(fā)生偏向撤銷操作時(shí),該計(jì)數(shù)器+1,當(dāng)這個(gè)值達(dá)到重偏向閾值(默認(rèn)20)時(shí),JVM就認(rèn)為該class的偏向鎖有問題,因此會(huì)進(jìn)行批量重偏向。每個(gè)class對(duì)象會(huì)有一個(gè)對(duì)應(yīng)的epoch字段,每個(gè)處于偏向鎖狀態(tài)對(duì)象的mark word中也有該字段,其初始值為創(chuàng)建該對(duì)象時(shí),class中的epoch的值。每次發(fā)生批量重偏向時(shí),就將該值+1,同時(shí)遍歷JVM中所有線程的棧,找到該class所有正處于加鎖狀態(tài)的偏向鎖,將其epoch字段改為新值。下次獲得鎖時(shí),發(fā)現(xiàn)當(dāng)前對(duì)象的epoch值和class的epoch不相等,那就算當(dāng)前已經(jīng)偏向了其他線程,也不會(huì)執(zhí)行撤銷操作,而是直接通過CAS操作將其mark word的Thread Id 改成當(dāng)前線程Id。

          當(dāng)達(dá)到重偏向閾值后,假設(shè)該class計(jì)數(shù)器繼續(xù)增長(zhǎng),當(dāng)其達(dá)到批量撤銷的閾值后(默認(rèn)40),JVM就認(rèn)為該class的使用場(chǎng)景存在多線程競(jìng)爭(zhēng),會(huì)標(biāo)記該class為不可偏向,之后,對(duì)于該class的鎖,直接走輕量級(jí)鎖的邏輯。

          三、總結(jié)

          Java中的synchronized有偏向鎖、輕量級(jí)鎖、重量級(jí)鎖三種形式,分別對(duì)應(yīng)了鎖只被一個(gè)線程持有、不同線程交替持有鎖、多線程競(jìng)爭(zhēng)鎖三種情況。當(dāng)條件不滿足時(shí),鎖會(huì)按偏向鎖->輕量級(jí)鎖->重量級(jí)鎖 的順序升級(jí)。JVM種的鎖也是能降級(jí)的,只不過條件很苛刻,不在我們討論范圍之內(nèi)。該篇文章主要是對(duì)Java的synchronized做個(gè)基本介紹,后文會(huì)有更詳細(xì)的分析。


          源:http://suo.im/6h5g96

          版權(quán)申明:內(nèi)容來源網(wǎng)絡(luò),版權(quán)歸原創(chuàng)者所有。除非無法確認(rèn),我們都會(huì)標(biāo)明作者及出處,如有侵權(quán)煩請(qǐng)告知,我們會(huì)立即刪除并表示歉意。謝謝!





          感謝閱讀



          瀏覽 53
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  日韩黄色电影免费观看 | A级黄片在线视频 | 色婷婷国产精品综合在线观看 | 好屌视频一区二区三区 | 久久综合久色欧美综合狠狠 |