面試重災區——Synchronized深度解析

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"兄弟們,大家好。時隔多天,我,終於來了。今天我們來聊一下讓人神魂顛倒的"},{"type":"codeinline","content":[{"type":"text","text":"Synchronized"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過呢,在讀這篇文章之前,我希望你真正使用過這個東東,或者瞭解它到底是幹嘛用的,不然很難理解這篇文章講解的東西。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這篇文章的大體順序是:從"},{"type":"text","marks":[{"type":"strong"}],"text":"無鎖-->偏向鎖-->輕量級鎖-->重量級鎖"},{"type":"text","text":"講解,其中會涉及到"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":","},{"type":"text","marks":[{"type":"strong"}],"text":"對象內存佈局"},{"type":"text","text":",緩存行等等知識點。也是滿滿的乾貨內容。其中也夾雜了個人在面試過程中出現的面試題,各位兄弟慢慢享受。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Synchronized"}]},{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"jdk1.6"}]},{"type":"text","text":"做了非常大的優化,避免了很多時候的用戶態到內核態的切換,節省了資源的開銷,而這一切的前提均來源於"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"這個理念。下面我們先來聊一下"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"的一些基本理論。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. CAS"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"全稱:"},{"type":"codeinline","content":[{"type":"text","text":"CompareAndSwap"}]},{"type":"text","text":",故名思意:比較並交換。他的主要思想就是:"},{"type":"text","marks":[{"type":"strong"}],"text":"我需要對一個值進行修改,我不會直接修改,而是將當前我認爲的值和要修改的值傳入,如果此時內存中的確爲我認爲的值,那麼就進行修改,否則修改失敗。"},{"type":"text","text":"他的思想是一種樂觀鎖的思想。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一張圖解釋他的工作流程:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2b/2bd5fe23888598f217b75f5ee35a5ac9.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"知道了它的工作原理,我們來聽一個場景:**現在有一個"},{"type":"codeinline","content":[{"type":"text","text":"int"}]},{"type":"text","text":"類型的數字它等於1,存在三個線程需要對其進行自增操作。**"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般來說,我們認爲的操作步驟是這樣:線程從主內存中讀取這個變量,到自己的工作空間中,然後執行變量自增,然後回寫主內存,但這樣在多線程狀態下會存在安全問題。**而如果我們保證變量的安全性,常用的做法是"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"或者直接加鎖。**(對"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"不瞭解的兄弟,看我這篇文章"},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzU5NzMxNDE5NA==&mid=2247484744&idx=1&sn=641c5b2c2261fe7a82bd314f724deada&chksm=fe541ab5c92393a377edbb4be3b80d028f1228589af584892d9bbb850f381e74ddcbdb9a704b&token=1308274375&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"一文讀懂ThreadLocal設計思想"}]},{"type":"text","text":")"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個時候我們思考一下,如果使用我們上面的"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"進行對值的修改,我們需要如何操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"首先,我們需要將當前線程認爲的值傳入,然後將想要修改的值傳入。如果此時內存中的值和我們的期望值相等,進行修改,否則修改失敗。這樣是不是解決了一個多線程修改的問題,而且它沒有使用到操作系統提供的鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的流程其實就是類"},{"type":"codeinline","content":[{"type":"text","text":"AtomicInteger"}]},{"type":"text","text":"執行自增操作的底層實現,它保證了一個操作的原子性。我們來看一下源碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public final int incrementAndGet() {\n return unsafe.getAndAddInt(this, valueOffset, 1) + 1;\n }\n\npublic final int getAndAddInt(Object var1, long var2, int var4) {\n int var5;\n do {\n //從內存中讀取最新值\n var5 = this.getIntVolatile(var1, var2);\n //修改\n } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));\n\n return var5;\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實現"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"使用到了"},{"type":"codeinline","content":[{"type":"text","text":"Unsafe"}]},{"type":"text","text":"類,看它的名字就知道不安全,所以"},{"type":"codeinline","content":[{"type":"text","text":"JDK"}]},{"type":"text","text":"不建議我們使用。對比我們上面多個線程執行一個變量的修改流程,這個類的操作僅僅增加了一個自旋,它在不斷獲取內存中的最新值,然後執行自增操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可能有兄弟說了,那"},{"type":"codeinline","content":[{"type":"text","text":"getIntVolatile"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"compareAndSwapInt"}]},{"type":"text","text":"操作如何保證原子性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於"},{"type":"codeinline","content":[{"type":"text","text":"getIntVolatile"}]},{"type":"text","text":"來說,讀取內存中的地址,本來就一部操作,原子性顯而易見。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於"},{"type":"codeinline","content":[{"type":"text","text":"compareAndSwapInt"}]},{"type":"text","text":"來說,它的原子性由"},{"type":"codeinline","content":[{"type":"text","text":"CPU"}]},{"type":"text","text":"保證,通過一系列的"},{"type":"codeinline","content":[{"type":"text","text":"CPU"}]},{"type":"text","text":"指令實現,其"},{"type":"codeinline","content":[{"type":"text","text":"C++"}]},{"type":"text","text":"底層是依賴於"},{"type":"codeinline","content":[{"type":"text","text":"Atomic::cmpxchg_ptr"}]},{"type":"text","text":"實現的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到這裏"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"講完了,不過其中還有一個"},{"type":"codeinline","content":[{"type":"text","text":"ABA"}]},{"type":"text","text":"問題,有興趣可以去了解我的這篇文章"},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzU5NzMxNDE5NA==&mid=2247484523&idx=1&sn=099d662b6a1758036f10dd77d3931c71&chksm=fe541b96c9239280e9eed5842bd27b21a374dcd9ce6bce1bb64324f685f3dd9e32413d333d31&token=1308274375&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"多線程知識點小節"}]},{"type":"text","text":"。裏面有詳細的講解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們通過"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"可以保證了操作的原子性,那麼我們需要考慮一個東西,鎖是怎麼實現的。對比生活中的"},{"type":"codeinline","content":[{"type":"text","text":"case"}]},{"type":"text","text":",我們通過一組密碼或者一把鑰匙實現了一把鎖,同樣在計算機中也通過一個鑰匙即"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":"代碼塊使用的鎖對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那其他線程如何判斷當前資源已經被佔有了呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**在計算機中的實現,往往是通過對一個變量的判斷來實現,無鎖狀態爲"},{"type":"codeinline","content":[{"type":"text","text":"0"}]},{"type":"text","text":",有鎖狀態爲"},{"type":"codeinline","content":[{"type":"text","text":"1"}]},{"type":"text","text":"等等來判斷這個資源是否被加鎖了,當一個線程釋放鎖時僅僅需要將這個變量值更改爲0,代表無鎖。**"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**我們僅僅需要保證在進行變量修改時的原子性即可,而剛剛的"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"剛好可以解決這個問題**"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"至於那個鎖變量存儲在哪裏這個問題,就是下面的內容了,對象的內存佈局"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 內存佈局"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"各位兄弟們,應該都清楚,我們創建的對象都是被存放到堆中的,最後我們獲得到的是一個對象的引用指針。那麼有一個問題就會誕生了,"},{"type":"codeinline","content":[{"type":"text","text":"JVM"}]},{"type":"text","text":"創建的對象的時候,開闢了一塊空間,那這個空間裏都有什麼東西?這個就是我們這個點的內容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先來結論:**"},{"type":"codeinline","content":[{"type":"text","text":"Java"}]},{"type":"text","text":"中存在兩種類型的對象,一種是普通對象,另一種是數組**"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"對象內存佈局"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/55/55b36054d7bbf5577b2138b5b12e4382.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來一個一個解釋其含義。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"白話版:"},{"type":"text","text":"對象頭中包含又兩個字段,"},{"type":"codeinline","content":[{"type":"text","text":"Mark Word"}]},{"type":"text","text":"主要存儲改對象的鎖信息,"},{"type":"codeinline","content":[{"type":"text","text":"GC"}]},{"type":"text","text":"信息等等(鎖升級的實現)。而其中的"},{"type":"codeinline","content":[{"type":"text","text":"Klass Point"}]},{"type":"text","text":"代表的是一個類指針,它指向了方法區中類的定義和結構信息。而"},{"type":"codeinline","content":[{"type":"text","text":"Instance Data"}]},{"type":"text","text":"代表的就是類的成員變量。在我們剛剛學習"},{"type":"codeinline","content":[{"type":"text","text":"Java"}]},{"type":"text","text":"基礎的時候,都聽過老師講過,對象的非靜態成員屬性都會被存放在堆中,這個就是對象的"},{"type":"codeinline","content":[{"type":"text","text":"Instance Data"}]},{"type":"text","text":"。相對於對象而言,數組額外添加了一個數組長度的屬性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"最後一個對其數據是什麼?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們拿一個場景來展示這個原因:"},{"type":"text","marks":[{"type":"strong"}],"text":"想像一下,你和女朋友週末打算出去玩,女朋友讓你給她帶上口紅,那麼這個時候你僅僅會帶上口紅嘛?當然不是,而是將所有的必用品統統帶上,以防剛一出門就得回家拿東西!!!"},{"type":"text","text":"這種行爲叫啥?"},{"type":"text","marks":[{"type":"strong"}],"text":"未雨綢繆,沒錯,暖男行爲"},{"type":"text","text":"。還不懂?再來一個案例。"},{"type":"text","marks":[{"type":"strong"}],"text":"你準備創業了,資金非常充足,你需要註冊一個域名,你僅僅註冊一個嘛?不,而是將所有相關的都註冊了,防止以後大價錢買域名"},{"type":"text","text":"。一個道理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而對於"},{"type":"codeinline","content":[{"type":"text","text":"CPU"}]},{"type":"text","text":"而言,它在進行計算處理數據的時候,不可能需要什麼拿什麼吧,那對其性能損耗非常嚴重。所以有一個協議,**"},{"type":"codeinline","content":[{"type":"text","text":"CPU"}]},{"type":"text","text":"在讀取數據的時候,不僅僅只拿需要的數據,而是獲取一行的數據,這就是緩存行,而一行是64個字節**。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以呢?通過這個特性可以玩一些詭異的花樣,比如下面的代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class CacheLine {\n private volatile Long l1 , l2;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們給一個場景:兩個線程"},{"type":"codeinline","content":[{"type":"text","text":"t1和t2"}]},{"type":"text","text":"分別操作"},{"type":"codeinline","content":[{"type":"text","text":"l1"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"l2"}]},{"type":"text","text":",那麼當"},{"type":"codeinline","content":[{"type":"text","text":"t1"}]},{"type":"text","text":"對"},{"type":"codeinline","content":[{"type":"text","text":"l1"}]},{"type":"text","text":"做了修改以後,"},{"type":"codeinline","content":[{"type":"text","text":"l2"}]},{"type":"text","text":"需不需要重新讀取主內存種值。答案是一定,根據我們上面對於緩存行的理解,"},{"type":"codeinline","content":[{"type":"text","text":"l1和l2"}]},{"type":"text","text":"必然位於同一個緩存行中,根據緩存一致性協議,當數據被修改以後,其他"},{"type":"codeinline","content":[{"type":"text","text":"CPU"}]},{"type":"text","text":"需要重新重主內存中讀取數據。"},{"type":"text","marks":[{"type":"strong"}],"text":"這就引發了僞共享的問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"那麼爲什麼對象頭要求會存在一個對其數據呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"HotSpot"}]},{"type":"text","text":"虛擬機要求每一個對象的內存大小必須保證爲8字節的整數倍,所以對於不是8字節的進行了對其補充。其原因也是因爲緩存行的原因"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"對象=對象頭+實例數據"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. 無鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在前面聊了一下,計算機中的鎖的實現思路和對象在內存中的佈局,接下來我們來聊一下它的具體鎖實現,爲對象加鎖使用的是對象內存模型中的對象頭,"},{"type":"text","marks":[{"type":"strong"}],"text":"通過對其鎖標誌位和偏向鎖標誌位的修改實現對資源的獨佔即加鎖操作"},{"type":"text","text":"。接下來我們看一下它的內存結構圖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a8/a843ce0e58eff7844aa6970abddf5927.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上圖就是對象頭在內存中的表現(64位),"},{"type":"codeinline","content":[{"type":"text","text":"JVM"}]},{"type":"text","text":"通過對對象頭中的鎖標誌位和偏向鎖位的修改實現“無鎖”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於無鎖這個概念來說,在"},{"type":"codeinline","content":[{"type":"text","text":"1.6"}]},{"type":"text","text":"之前,即所有的對象,被創建了以後都處於無鎖狀態,而在"},{"type":"codeinline","content":[{"type":"text","text":"1.6"}]},{"type":"text","text":"之後,偏向鎖被開啓,對象在經歷過幾秒的時候(4~5s)以後,自動升級爲當前線程的偏向鎖。(無論經沒經過"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":")。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來驗證一下,通過"},{"type":"codeinline","content":[{"type":"text","text":"jol-core"}]},{"type":"text","text":"工具打印其內存佈局。"},{"type":"text","marks":[{"type":"strong"}],"text":"注:該工具打印出來的數據信息是反的,即最後幾位在前面,通過下面的案例可以看到"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景:創建兩個對象,一個在剛開始的時候就創建,另一個在"},{"type":"codeinline","content":[{"type":"text","text":"5"}]},{"type":"text","text":"秒之後創建,進行對比其內存佈局"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Object object = new Object();\nSystem.out.println(ClassLayout.parseInstance(object).toPrintable());//此時處於無鎖態\ntry {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}\nObject o = new Object();\nSystem.out.println(\"偏向鎖開啓\");\nSystem.out.println(ClassLayout.parseInstance(o).toPrintable());//五秒以後偏向鎖開啓"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/44/4412e22bc258deb324c31ff990169915.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以看到,線程已開啓創建的對象處於無鎖態,而在5秒以後創建的線程處於偏向鎖狀態。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣,當我們遇到"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":"塊的時候,也會自動升級爲偏向鎖,而不是和操作系統申請鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"說完這個,提一嘴一個面試題吧。"},{"type":"text","marks":[{"type":"strong"}],"text":"解釋一下什麼是無鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從對象內存結構的角度來說,是一個鎖標誌位的體現;從其語義來說,無鎖這個比較抽象了,因爲在以前鎖的概念往往是與操作系統的鎖息息相關,所以新出現的基於"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"的偏向鎖,輕量級鎖等等也被成爲無鎖。而在"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":"升級的起點----無鎖。這個東西就比較難以解釋,只能說它沒加鎖。不過面試的過程中從對象內存模型中理解可能會更加舒服一點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4. 偏向鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在實際開發中,往往資源的競爭比較少,於是出現了偏向鎖,故名思意,當前資源偏向於該線程,認爲將來的一切操作均來自於改線程。下面我們從對象的內存佈局下看看偏向鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"對象頭描述:偏向鎖標誌位通過CAS修改爲1,並且存儲該線程的線程指針"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a8/a843ce0e58eff7844aa6970abddf5927.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當發生了鎖競爭,其實也不算鎖競爭,就是當這個資源被多個線程使用的時候,偏向鎖就會升級。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在升級的期間有一個點-----"},{"type":"text","marks":[{"type":"strong"}],"text":"全局安全點"},{"type":"text","text":",只有處在這個點的時候,纔會撤銷偏向鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"全局安全點-----類似於"},{"type":"codeinline","content":[{"type":"text","text":"CMS"}]},{"type":"text","text":"的"},{"type":"codeinline","content":[{"type":"text","text":"stop the world"}]},{"type":"text","text":",保證這個時候沒有任何線程在操作這個資源,這個時間點就叫做全局安全點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以通過"},{"type":"codeinline","content":[{"type":"text","text":"XX:BiasedLockingStartupDelay=0 "}]},{"type":"text","text":"關閉偏向鎖的延遲,使其立即生效。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過"},{"type":"codeinline","content":[{"type":"text","text":"XX:-UseBiasedLocking=false "}]},{"type":"text","text":"關閉偏向鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5.輕量級鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在聊輕量級鎖的時候,我們需要搞明白這幾個問題。"},{"type":"text","marks":[{"type":"strong"}],"text":"什麼是輕量級鎖,什麼重量級鎖?,爲什麼就重量了,爲什麼就輕量了?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"輕量級和重量級的標準是依靠於操作系統作爲標準判斷的,在進行操作的時候你有沒有調用過操作系統的鎖資源,如果有就是重量級,如果沒有就是輕量級"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來我們看一下輕量級鎖的實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程獲取鎖,判斷當前線程是否處於無鎖或者偏向鎖的狀態,如果是,通過"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"複製當前對象的對象頭到"},{"type":"codeinline","content":[{"type":"text","text":"Lock Recoder"}]},{"type":"text","text":"放置到當前棧幀中(對於"},{"type":"codeinline","content":[{"type":"text","text":"JVM"}]},{"type":"text","text":"內存模型不清楚的兄弟,看這裏"},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzU5NzMxNDE5NA==&mid=2247484527&idx=1&sn=e0b1896ffc6167b270c750d9407c0003&chksm=fe541b92c92392848d0e3670661c9b6aab86d412a6d1733cba755239d52b912d0302a7638814&token=1308274375&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"入門JVM看這一篇就夠了"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"將當前對象的對象頭設置爲棧幀中的"},{"type":"codeinline","content":[{"type":"text","text":"Lock Recoder"}]},{"type":"text","text":",並且將鎖標誌位設置爲"},{"type":"codeinline","content":[{"type":"text","text":"00"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果修改失敗,則判斷當前棧幀中的線程是否爲自己,如果是自己直接獲取鎖,如果不是升級爲重量級鎖,後面的線程阻塞"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在上面提到了一個"},{"type":"codeinline","content":[{"type":"text","text":"Lock Recoder"}]},{"type":"text","text":",這個東東是用來保存當前對象的對象頭中的數據的,並且此時在該對象的對象頭中保存的數據成爲了當前"},{"type":"codeinline","content":[{"type":"text","text":"Lock Recoder"}]},{"type":"text","text":"的指針"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/81/815c3eccac3dd374194832e3369de89d.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們看一個代碼模擬案例,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class QingLock {\n public static void main(String[] args) {\n try {\n //睡覺5秒,開啓偏向鎖,可以使用JVM參數\n TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}\n A o = new A();\n //讓線程交替執行\n CountDownLatch countDownLatch = new CountDownLatch(1);\n new Thread(()->{\n o.test();\n countDownLatch.countDown();\n },\"1\").start();\n\n new Thread(()->{\n try {\n countDownLatch.await();\n } catch (InterruptedException e) {\n e.printStackTrace();\n }\n o.test();\n },\"2\").start();\n\n\n }c++\n}\n\nclass A{\n private Object object = new Object();\n public void test(){\n System.out.println(\"爲進入同步代碼塊*****\");\n System.out.println(ClassLayout.parseInstance(object).toPrintable());\n System.out.println(\"進入同步代碼塊******\");\n for (int i = 0; i < 5; i++) {\n synchronized (object){\n System.out.println(ClassLayout.parseInstance(object).toPrintable());\n }\n }\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"運行結果爲兩個線程交替前後"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c6/c69f2c5cbc714cf46367be41d3f4841c.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"輕量級鎖強調的是線程交替使用資源,無論線程的個數有幾個,只要沒有同時使用就不會升級爲重量級鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上面的關於輕量級鎖加鎖步驟的講解中,如果線程"},{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"修改失敗,則判斷棧幀中的"},{"type":"codeinline","content":[{"type":"text","text":"owner"}]},{"type":"text","text":"是不是自己,如果不是就失敗升級爲重量級鎖,而在實際中,"},{"type":"codeinline","content":[{"type":"text","text":"JDK"}]},{"type":"text","text":"加入了一種機制**自旋鎖**,即修改失敗以後不會立即升級而是進行自旋,在"},{"type":"codeinline","content":[{"type":"text","text":"JDK1.6"}]},{"type":"text","text":"之前自旋次數爲"},{"type":"codeinline","content":[{"type":"text","text":"10"}]},{"type":"text","text":"次,而在"},{"type":"codeinline","content":[{"type":"text","text":"1.6"}]},{"type":"text","text":"又做了優化,改爲了"},{"type":"text","marks":[{"type":"strong"}],"text":"自適應自旋鎖"},{"type":"text","text":",由虛擬機判斷是否需要進行自旋,判斷原因有:"},{"type":"text","marks":[{"type":"strong"}],"text":"當前線程之前是否獲取到過鎖,如果沒有,則認爲獲取鎖的機率不大,直接升級,如果有則進行自旋獲取鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6. 重量級鎖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面我們談到了無鎖-->偏向鎖-->輕量級鎖,現在最後我們來聊一下重量級鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個鎖在我們開發過程中很常見,線程搶佔資源大部分都是同時的,所以"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":"會直接升級爲重量級鎖。我們來代碼模擬看一下它的對象頭的狀況。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"代碼模擬"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class WeightLock {\n public static void main(String[] args) {\n A a = new A();\n for (int i = 0; i < 2; i++) {\n new Thread(()->{\n a.test();\n },\"線程\"+ i).start();\n }\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"未進入代碼塊之前,兩者均爲無鎖狀態"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/01/0109b2d70c892af8557f0d983092864b.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"開始執行循環,進入代碼塊"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/66/66e56c7b78ae7e9fe74b2d0d3c5f00f0.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"在看一眼,對象頭鎖標誌位"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a8/a843ce0e58eff7844aa6970abddf5927.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對比上圖,可以發現,在線程競爭的時候鎖,已經變爲了重量級鎖。接下來我們來看一下重量級鎖的實現"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"6.1 Java彙編碼分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先從"},{"type":"codeinline","content":[{"type":"text","text":"Java"}]},{"type":"text","text":"字節碼分析"},{"type":"codeinline","content":[{"type":"text","text":"synchronzied"}]},{"type":"text","text":"的底層實現,它的主要實現邏輯是依賴於一個"},{"type":"codeinline","content":[{"type":"text","text":"monitor"}]},{"type":"text","text":"對象,當前線程執行遇到"},{"type":"codeinline","content":[{"type":"text","text":"monitorenter"}]},{"type":"text","text":"以後,給當前對象的一個屬性"},{"type":"codeinline","content":[{"type":"text","text":"recursions"}]},{"type":"text","text":"加一(下面會詳細講解),當遇到"},{"type":"codeinline","content":[{"type":"text","text":"monitorexit"}]},{"type":"text","text":"以後該屬性減一,代表釋放鎖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Object o = new Object();\nsynchronized (o){\n\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"彙編碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/07/0799e971f27f4c4bef4dd8f2264b46cf.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上圖就是上面的四行代碼的彙編碼,我們可以看到"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":"的底層是兩個彙編指令"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"monitoreneter"}]},{"type":"text","text":"代表"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":"塊開始"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"monitorexit"}]},{"type":"text","text":"代表"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":"塊結束"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有兄弟要說了**爲什麼會有兩個"},{"type":"codeinline","content":[{"type":"text","text":"monitorexit"}]},{"type":"text","text":"?這也是我曾經遇到的一個面試題**"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一個"},{"type":"codeinline","content":[{"type":"text","text":"monitorexit"}]},{"type":"text","text":"代表了"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":"塊正常退出"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二個"},{"type":"codeinline","content":[{"type":"text","text":"monitorexit"}]},{"type":"text","text":"代表了"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":"塊異常退出"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很好理解,當在"},{"type":"codeinline","content":[{"type":"text","text":"synchronized"}]},{"type":"text","text":"塊中出現了異常以後,不能當前線程一直拿着鎖不讓其他線程使用吧。所以出現了兩個"},{"type":"codeinline","content":[{"type":"text","text":"monitorexit"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"同步代碼塊理解了,我們再來看一下同步方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static void main(String[] args) {\n\n}\n\npublic synchronized void test01(){\n\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"彙編碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3a/3adf38b86d70ffabab3896f9123bebcd.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以看到,同步方法增加了一個"},{"type":"codeinline","content":[{"type":"text","text":"ACC_SYNCHRONIZED"}]},{"type":"text","text":"標誌,它會在同步方法執行之前調用"},{"type":"codeinline","content":[{"type":"text","text":"monitorenter"}]},{"type":"text","text":",結束以後調用"},{"type":"codeinline","content":[{"type":"text","text":"monitorexit"}]},{"type":"text","text":"指令。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"6.2 C++代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"Java"}]},{"type":"text","text":"彙編碼的講解中,我們提到了兩個指令"},{"type":"codeinline","content":[{"type":"text","text":"monitorenter"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"monitorexit"}]},{"type":"text","text":",其實他們是來源於一個"},{"type":"codeinline","content":[{"type":"text","text":"C++"}]},{"type":"text","text":"對象"},{"type":"codeinline","content":[{"type":"text","text":"monitor"}]},{"type":"text","text":",在"},{"type":"codeinline","content":[{"type":"text","text":"Java"}]},{"type":"text","text":"中每創建一個對象的時候都會有一個"},{"type":"codeinline","content":[{"type":"text","text":"monitor"}]},{"type":"text","text":"對象被隱式創建,他們和當前對象綁定,用於監視當前對象的狀態。其實說綁定也不算正確,其實際流程爲:**線程本身維護了兩個"},{"type":"codeinline","content":[{"type":"text","text":"MonitorList"}]},{"type":"text","text":"列表,分別爲空閒(free)和已經使用(used),當線程遇到同步代碼塊或者同步方法的時候,會從空閒列表中申請一個"},{"type":"codeinline","content":[{"type":"text","text":"monitor"}]},{"type":"text","text":"使用,如果當先線程已經沒有空閒的了,則直接從全局("},{"type":"codeinline","content":[{"type":"text","text":"JVM"}]},{"type":"text","text":")獲取一個"},{"type":"codeinline","content":[{"type":"text","text":"monitor"}]},{"type":"text","text":"使用**"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來看一下"},{"type":"codeinline","content":[{"type":"text","text":"C++"}]},{"type":"text","text":"對這個對象的描述"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"ObjectMonitor() {\n\n _header = NULL;\n\n _count = 0;\n\n _waiters = 0,\n\n _recursions = 0; // 重入次數\n\n _object = NULL; //存儲該Monitor對象\n\n _owner = NULL; //擁有該Monitor對象的對象\n\n _WaitSet = NULL; //線程等待集合(Waiting)\n\n _WaitSetLock = 0 ;\n\n _Responsible = NULL ;\n\n _succ = NULL ;\n\n _cxq = NULL ; //多線程競爭時的單向鏈表\n\n FreeNext = NULL ;\n\n _EntryList = NULL ; //阻塞鏈表(Block)\n\n _SpinFreq = 0 ;\n\n _SpinClock = 0 ;\n\n OwnerIsThread = 0 ;\n\n previousowner_tid = 0;\n\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"線程加鎖模型"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e9/e9c1a3283bf04c4b25ee82aafadf8741.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"加鎖流程:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最新進入的線程會進入"},{"type":"codeinline","content":[{"type":"text","text":"_cxp"}]},{"type":"text","text":"棧中,嘗試獲取鎖,如果當前線程獲得鎖就執行代碼,如果沒有獲取到鎖則添加到"},{"type":"codeinline","content":[{"type":"text","text":"EntryList"}]},{"type":"text","text":"阻塞隊列中"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果在執行的過程的當前線程被掛起("},{"type":"codeinline","content":[{"type":"text","text":"wait"}]},{"type":"text","text":")則被添加到"},{"type":"codeinline","content":[{"type":"text","text":"WaitSet"}]},{"type":"text","text":"等待隊列中,等待被喚醒繼續執行"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當同步代碼塊執行完畢以後,從"},{"type":"codeinline","content":[{"type":"text","text":"_cxp"}]},{"type":"text","text":"或者"},{"type":"codeinline","content":[{"type":"text","text":"EntryList"}]},{"type":"text","text":"中獲取一個線程執行"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**"},{"type":"codeinline","content":[{"type":"text","text":"monitorenter"}]},{"type":"text","text":"加鎖實現**"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"CAS"}]},{"type":"text","text":"修改當前"},{"type":"codeinline","content":[{"type":"text","text":"monitor"}]},{"type":"text","text":"對象的"},{"type":"codeinline","content":[{"type":"text","text":"_owner"}]},{"type":"text","text":"爲當前線程,如果修改成功,執行操作;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果修改失敗,判斷"},{"type":"codeinline","content":[{"type":"text","text":"_owner"}]},{"type":"text","text":"對象是否爲當前線程,如果是則令"},{"type":"codeinline","content":[{"type":"text","text":"_recursions"}]},{"type":"text","text":"重入次數加一"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果當前實現是第一次獲取到鎖,則將"},{"type":"codeinline","content":[{"type":"text","text":"_recursions"}]},{"type":"text","text":"設置爲一"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"等待鎖釋放"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"阻塞和獲取鎖實現"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將當前線程封裝爲一個"},{"type":"codeinline","content":[{"type":"text","text":"node"}]},{"type":"text","text":"節點,狀態設置爲"},{"type":"codeinline","content":[{"type":"text","text":"ObjectWaiter::TS_CXQ"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將之添加到"},{"type":"codeinline","content":[{"type":"text","text":"_cxp"}]},{"type":"text","text":"棧中,嘗試獲取鎖,如果獲取失敗,則將當前線程掛起,等待喚醒"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"喚醒以後,從掛起點執行剩下的代碼"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**"},{"type":"codeinline","content":[{"type":"text","text":"monitorexit"}]},{"type":"text","text":"釋放鎖實現**"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讓當前線程的"},{"type":"codeinline","content":[{"type":"text","text":"_recursions"}]},{"type":"text","text":"重入次數減一,如果當前重入次數爲0,則直接退出,喚醒其他線程"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"參考資料:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"馬士兵多線程技術詳解書籍"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HotSpot源碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"往期推薦:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzU5NzMxNDE5NA==&mid=2247484676&idx=1&sn=4eaf632c4452b9f1124351be165e2a55&chksm=fe541af9c92393ef97f43b2914e5389990f88220b6e0ce55305a8fffe9f2aa4fce1f32552c6c&token=1308274375&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"一文帶你瞭解Spring MVC的架構思路"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzU5NzMxNDE5NA==&mid=2247484608&idx=1&sn=c5f5ebe27e85657a9a1d4ea4842012a5&chksm=fe541b3dc923922bc7b0b96808efa49976e5bdf0c2af78ee8bf83c0591735aa6da0be4aac51d&token=1308274375&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"Mybatis你只會CRUD嘛"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzU5NzMxNDE5NA==&mid=2247484651&idx=1&sn=5e041c6f17d4c0f2dba9722c7b263006&chksm=fe541b16c9239200066d6b25fa28fd79e773fe4e26571f0a9def2023c7cbfa0e0003deb9dc6f&token=1308274375&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"IOC的架構你瞭解嘛"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"筆者整理了近100+的技術類書籍和個人學習總結,關注公衆號@MakerStack領取。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章