Synchronized是王的后宮總管,線程是王妃
假如 synchronized 是「王」身邊的「大總管」,那么 Thread 就像是他后宮的王妃。「王」每日只能選擇一個(gè)王妃陪伴,王妃們會(huì)想方設(shè)法爭(zhēng)寵獲得陪伴權(quán),大總管需要通過一定的手段讓王「翻牌」一個(gè)「王妃」與王相伴。
在JMM 透析 volatile 與 synchronized 原理一文中講解了內(nèi)存模型與并發(fā)實(shí)現(xiàn)原理的深層關(guān)系,今日聽「碼哥」胡言亂語(yǔ)解開 synchronized 大總管如何調(diào)度「王妃」陪伴「王」,王妃不同狀態(tài)的變化到底經(jīng)歷了什么?且看 synchronized 大總管又采取了哪些手段更加高效「翻牌」一個(gè)王妃,宮斗還有 30 秒到達(dá)戰(zhàn)場(chǎng)!!!
- 王妃的 6 種狀態(tài)
- synchronized 總管如何提升效率翻牌
- 自適應(yīng)自旋
- 鎖消除
- 鎖粗化
- 偏向鎖/輕量級(jí)鎖/重量級(jí)鎖
「碼哥字節(jié)」講通過幾個(gè)故事,通俗易懂的讓讀者朋友完全掌握 synchronized 的鎖優(yōu)化(偏向鎖 -> 輕量級(jí)鎖 -> 重量級(jí)鎖)原理以及線程在 6 種狀態(tài)之間轉(zhuǎn)換的奧秘。
抽象出三個(gè)概念:Thread 對(duì)應(yīng)后宮佳麗「王妃」,synchronized 是后宮大總管負(fù)責(zé)安排調(diào)度王妃,「王」則是被王妃們想要競(jìng)爭(zhēng)的資源。
王妃的 6 種狀態(tài)
后宮佳麗等級(jí)森嚴(yán),王妃們?cè)谶@場(chǎng)權(quán)貴的游戲中每個(gè)人的目的都是為獲取「王」寵愛,在游戲中自身的狀態(tài)也隨著變化。
就像生物從出生到長(zhǎng)大、最終死亡的過程一樣,「王妃」也有自己的生命周期,在 王妃的生命周期中一共有 6 種狀態(tài)。
- New(新入宮):Thread state for a thread which has not yet started.
- Runnable 可運(yùn)行、就緒:(身體舒適,準(zhǔn)備好了),Java 中的 Runable 狀態(tài)對(duì)應(yīng)操作系統(tǒng)線程狀態(tài)中的兩種狀態(tài),分別是 Running 和 Ready,也就是說(shuō),Java 中處于 Runnable 狀態(tài)的線程有可能正在執(zhí)行,也有可能沒有正在執(zhí)行,正在等待被分配 CPU 資源。
- Blocked 阻塞(身體欠佳、被打入冷宮)
- WAITING(等待):(等待傳喚)
- Timed Waiting(計(jì)時(shí)等待):在門外計(jì)時(shí)等待
- Terminated(終止):嗝屁
線程狀態(tài)
王妃在任意時(shí)刻只能是其中的一種狀態(tài),通過 getState() 方法獲取線程狀態(tài)。
New 新入宮
第一日
「王」微服私訪,駕車游玩,途徑桃花源。見風(fēng)景宜人,如人間仙境。停車坐愛楓林晚,霜葉紅于二月花。此時(shí)此刻,一女子媚眼含羞合,丹唇逐笑開。風(fēng)卷葡萄帶,日照石榴裙。從「王」面前走過,遭遇惡人。「王」拿起雙截棍哼哼哈嘿,飛檐走壁制服惡人,美人入懷,「香妃」便初入王宮。
「王」擬寫一份招入宮的詔書,上面寫著new Thread() ,「香妃」的名分便正式成立。New 表示線程被創(chuàng)建但是還沒有啟動(dòng)的狀態(tài),猶如「香妃」剛剛?cè)雽m,等待她后面的路程將是驚心動(dòng)魄,蕩氣回腸。
此刻 「皇后」(可以理解是 JVM)命令趙公公,為「香妃」分配寢宮(也就是分配內(nèi)存),并初始化其身邊的「丫鬟」(初始化成員變量的值)。
Runnable 可運(yùn)行、就緒
「香妃」獲得「王」的詔書,安排好衣食住行之后,便準(zhǔn)備好陪伴王了。但是后宮佳麗很多,并不是所有人都能獲得陪伴權(quán),「香妃」早已準(zhǔn)備好,也在爭(zhēng)取可以獲得與「王」共舞的機(jī)會(huì)。便主動(dòng)告知趙公公,自己琴棋書畫樣樣精通。希望得到安排,所以便被趙公公調(diào)度。「皇后」安排丫鬟為「香妃」沐浴更衣,抹上胭脂等待召喚(相當(dāng)于線程的 start() 方法被調(diào)用)。(Java 虛擬機(jī)會(huì)為其創(chuàng)建方法調(diào)用棧可程序計(jì)數(shù)器,等到調(diào)度運(yùn)行)此刻線程就處于可運(yùn)行狀態(tài)。
Java 中的 Runable 狀態(tài)對(duì)應(yīng)操作系統(tǒng)線程狀態(tài)中的兩種狀態(tài),分別是 Running 和 Ready,也就是說(shuō),Java 中處于 Runnable 狀態(tài)的線程有可能正在執(zhí)行,也有可能沒有正在執(zhí)行,正在等待被分配 CPU 資源。
如果一個(gè)正在運(yùn)行的線程是 Runnable 狀態(tài),當(dāng)它運(yùn)行到任務(wù)的一半時(shí),執(zhí)行該線程的 CPU 被調(diào)度去做其他事情,導(dǎo)致該線程暫時(shí)不運(yùn)行,它的狀態(tài)依然不變,還是 Runnable,因?yàn)樗锌赡茈S時(shí)被調(diào)度回來(lái)繼續(xù)執(zhí)行任務(wù)。
注意:?jiǎn)?dòng)線程使用 start() 方法,而不是 run() 方法。調(diào)用 start()方法啟動(dòng)線程,系統(tǒng)會(huì)把該 run方法當(dāng)成方法執(zhí)行體處理。需要切記的是:調(diào)用了線程的 run()方法之后,該線程就不在處于新建狀態(tài),不要再次調(diào)用 start()方法,只能對(duì)新建狀態(tài)的線程調(diào)用start()方法,否則會(huì)引發(fā) IllegaIThreadStateExccption異常。
「香妃」沐浴更衣之后(被調(diào)用start() )便焚香撫琴,「淑妃」不跟示弱起舞弄影競(jìng)爭(zhēng)陪伴權(quán),「香妃」畢竟新來(lái)的,喜新厭舊的渣男「王」甚是寵愛,「香妃」獲得了今晚的陪伴權(quán),獲得「皇后」給予 CPU 分片后,執(zhí)行 run()方法,該方法核心功能就是造娃…..
在造娃之前,「香妃」經(jīng)歷了很多紛爭(zhēng),狀態(tài)也一直在變化。稍有不慎,可能會(huì)進(jìn)入 TERMINATED 狀態(tài),直接玩完。請(qǐng)繼續(xù)閱讀……
Waiting 等待、Timed Waiting 計(jì)時(shí)等待、Blocked 阻塞
原先被得寵的「淑妃」敗給了新人「香妃」。當(dāng)進(jìn)入寢宮之時(shí),準(zhǔn)備造娃,王有要事需要處理,便使用了 Object.wait() 技能卡,「香妃」只能等待王回來(lái)……
王歸來(lái)為了解鎖之前對(duì)「香妃」釋放的Object.wait()技能便釋放 Object.notify() 解鎖卡通知「香妃」可以一起么么噠了,此刻「香妃」竟然大小便失禁觸發(fā)了 Thread.join() 只好去上廁所,讓老王稍等片刻。
「淑妃」當(dāng)晚雖然已經(jīng)處于Runnable 態(tài),但是被總管施展了 LockSupport.park() 技能卡,導(dǎo)致無(wú)法進(jìn)入寢宮,狀態(tài)由 Runnable 變成了 Waiting 態(tài)。今夜無(wú)緣老王!!!
「咔妃」由于太黑了,直接被 synchronized 大總管拒之門外,從 Runnable 變成 Blocked 。
還有其他「妃」被老王放鴿子了,跟他們說(shuō)三更之后見,這個(gè)時(shí)間管理,羅某人表示不服。她們分別被以下技能卡命中進(jìn)入,直接進(jìn)入 TIMED_WAITING 狀態(tài) :
- Thread.sleep:
- Object.wait with timeout
- Thread.join with timeout
- LockSupport.parkNanos
- LockSupport.parkUntil
第二日
言歸正傳, 昨日「香妃」大小便失禁觸發(fā)了 Thread.join() 上完廁所后,跟王一番云雨,終于天亮。
而被 synchronized 大總管拒之門外,從 Runnable 變成 Blocked 的「淑妃」在第二日得到老王的寵信。因?yàn)椤赶沐咕谷魂P(guān)鍵時(shí)刻大小便失禁,所以便想著找昨天差點(diǎn)被翻牌的「淑妃」。所以今日「淑妃」獲得了老王留給她的 monitor 鎖,得到了 syncronized 大總管的許可,由昨天的 Blocked 變成了Runnable ……
另外,有的王妃為了獲得陪伴權(quán),或者想掌管后宮。陰謀詭計(jì)被識(shí)破,被判處 Terminated 刑罰,滅頂之災(zāi),強(qiáng)擼灰飛煙滅!
synchronized 總管如何提升效率翻牌
王妃們除了使用 LockSupport.unpark() 等獲取陪伴權(quán),還可以通過由老王欽點(diǎn)大總管synchronized 去翻牌獲得陪伴權(quán)。面對(duì)三千佳麗,大總管必須要提高效率,不然將會(huì)累死而選不出一個(gè)王妃去陪伴老王,這可是要?dú)㈩^的。
因?yàn)樵?Java 5 版本之前,synchronized 的篩選方法效率很差,一堆王妃跑進(jìn)來(lái)吵著我行我上,秩序混亂,堪比 OFO 退押金現(xiàn)場(chǎng),上一任總管就被殺頭了……
到了第 6 任以后,做了很大改善。運(yùn)用了自適應(yīng)自旋、鎖消除、鎖粗化、輕量級(jí)鎖、偏向鎖,效率大大提升。
自適應(yīng)自旋
通知王妃們過來(lái)排隊(duì),或者重新叫一個(gè)王妃來(lái)都是需要操作系統(tǒng)切換 CPU來(lái)完成,耗費(fèi)時(shí)間。
為了讓當(dāng)前申請(qǐng)陪伴的咖妃“稍等一下”, synchronized 大總管會(huì)讓王妃自旋,因?yàn)橥跖c多爾袞處理軍事機(jī)密,很快就會(huì)回來(lái)。咖妃只需要每隔一段時(shí)間詢問大總管王是否歸來(lái),一旦王歸來(lái),那么自己就不需要進(jìn)入阻塞態(tài),獲得今日與王為伴。避免因?yàn)橐ネㄖ鄠€(gè)王妃來(lái)競(jìng)爭(zhēng)費(fèi)時(shí)費(fèi)力。
用一句話總結(jié)自旋鎖的好處,那就是自旋鎖用循環(huán)去不停地嘗試獲取鎖,讓線程始終處于 Runnable 狀態(tài),節(jié)省了線程狀態(tài)切換帶來(lái)的開銷。
以下是自旋與非自旋獲取鎖的過程:
自旋與非自旋
AtomicInteger
在 Java 1.5 版本及以上的并發(fā)包中,也就是 java.util.concurrent 的包中,里面的原子類基本都是自旋鎖的實(shí)現(xiàn)。我們看下 AtomicInteger 類的定義:
- public class AtomicInteger extends Number implements java.io.Serializable {
- private static final long serialVersionUID = 6214790243416807050L;
- // setup to use Unsafe.compareAndSwapInt for updates
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final long valueOffset;
- static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicInteger.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
- }
- private volatile int value;
- ......
- }
各屬性的作用:
- unsafe:獲取并操作內(nèi)存的數(shù)據(jù)。
- valueOffset:存儲(chǔ) value 在 AtomicInteger 中的偏移量。
- value:存儲(chǔ) AtomicInteger 的 int 值,該屬性需要借助 volatile 關(guān)鍵字保證其在線程間是可見的。
查看 AtomicInteger 的自增函數(shù) incrementAndGet() 的源碼時(shí),自增函數(shù)底層調(diào)用的是unsafe.getAndAddInt()。
但是由于JDK本身只有Unsafe.class,只通過class文件中的參數(shù)名,并不能很好的了解方法的作用,我們通過OpenJDK 8 來(lái)查看Unsafe的源碼:
- // JDK AtomicInteger 自增
- public final int getAndIncrement() {
- return unsafe.getAndAddInt(this, valueOffset, 1);
- }
- // OpenJDK 8
- // Unsafe.java
- public final int getAndAddInt(Object o, long offset, int delta) {
- int v;
- do {
- v = getIntVolatile(o, offset);
- } while (!compareAndSwapInt(o, offset, v, v + delta));
- return v;
- }
通過 do while 實(shí)現(xiàn)了自旋,getAndAddInt() 循環(huán)獲取給定對(duì)象 o 中的偏移量處的值 v,然后判斷內(nèi)存值是否等于 v。如果相等則將內(nèi)存值設(shè)置為 v + delta,否則返回false,繼續(xù)循環(huán)進(jìn)行重試,直到設(shè)置成功才能退出循環(huán),并且將舊值返回。
整個(gè)“比較 + 更新”操作封裝在 compareAndSwapInt() 中,在 JNI 里是借助于一個(gè) CPU 指令完成的,屬于原子操作,可以保證多個(gè)線程都能夠看到同一個(gè)變量的修改值。
synchronized 大總管在 1.6 版本想出了自適應(yīng)自旋鎖來(lái)解決長(zhǎng)時(shí)間自旋的問題,防止一直傻傻等待傻傻的問。會(huì)根據(jù)最近自旋的成功率、失敗率。
如果最近嘗試自旋獲取某一把鎖成功了,那么下一次可能還會(huì)繼續(xù)使用自旋,并且允許自旋更長(zhǎng)的時(shí)間;但是如果最近自旋獲取某一把鎖失敗了,那么可能會(huì)省略掉自旋的過程,以便減少無(wú)用的自旋,提高效率。
鎖消除
淑妃詭詐,在公元 107 年一個(gè)夜黑風(fēng)高的夜晚,串通后廚小哲子放了無(wú)色無(wú)味的黯然銷魂藥,一個(gè)個(gè)有氣無(wú)力。
所以只剩下自己一個(gè)人向 synchronized 大總管申請(qǐng),便不需要繁瑣流程,直搗黃龍。直接面見老王,無(wú)需加鎖。
鎖消除即刪除不必要的加鎖操作。虛擬機(jī)即時(shí)編輯器在運(yùn)行時(shí),對(duì)一些“代碼上要求同步,但是被檢測(cè)到不可能存在共享數(shù)據(jù)競(jìng)爭(zhēng)”的鎖進(jìn)行消除。
根據(jù)代碼逃逸技術(shù),如果判斷到一段代碼中,堆上的數(shù)據(jù)不會(huì)逃逸出當(dāng)前線程,那么可以認(rèn)為這段代碼是線程安全的,不必要加鎖。
- public class SynchronizedTest {
- public static void main(String[] args) {
- SynchronizedTest test = new SynchronizedTest();
- for (int i = 0; i < 100000000; i++) {
- test.append("碼哥字節(jié)", "def");
- }
- }
- public void append(String str1, String str2) {
- StringBuffer sb = new StringBuffer();
- sb.append(str1).append(str2);
- }
- }
雖然StringBuffer的append是一個(gè)同步方法,但是這段程序中的StringBuffer屬于一個(gè)局部變量,并且不會(huì)從該方法中逃逸出去(即StringBuffer sb的引用沒有傳遞到該方法外,不可能被其他線程拿到該引用),所以其實(shí)這過程是線程安全的,可以將鎖消除。
鎖粗化
甄嬛深得老王寵愛,被偏愛的都有恃無(wú)恐。每次進(jìn)出 synchronized 大總管張掛你的大門都需要驗(yàn)證是否獲得 monitor 鎖,甄嬛進(jìn)來(lái)后還喜歡出去外面走走過一會(huì)有進(jìn)來(lái)看幾眼老王又出去,大總管也不用每次都要驗(yàn)證,將限制的范圍就加大了,防止反復(fù)驗(yàn)證。
如果一系列的連續(xù)操作都對(duì)同一個(gè)對(duì)象反復(fù)加鎖和解鎖,甚至加鎖操作是出現(xiàn)在循環(huán)體中的,那即使沒有出現(xiàn)線程競(jìng)爭(zhēng),頻繁地進(jìn)行互斥同步操作也會(huì)導(dǎo)致不必要的性能損耗。
如果虛擬機(jī)檢測(cè)到有一串零碎的操作都是對(duì)同一對(duì)象的加鎖,將會(huì)把加鎖同步的范圍擴(kuò)展(粗化)到整個(gè)操作序列的外部。
- public class StringBufferTest {
- StringBuffer stringBuffer = new StringBuffer();
- public void append(){
- stringBuffer.append("關(guān)注");
- stringBuffer.append("公眾號(hào)");
- stringBuffer.append("碼哥字節(jié)");
- }
- }
每次調(diào)用stringBuffer.append方法都需要加鎖和解鎖,如果虛擬機(jī)檢測(cè)到有一系列連串的對(duì)同一個(gè)對(duì)象加鎖和解鎖操作,就會(huì)將其合并成一次范圍更大的加鎖和解鎖操作,即在第一次 append 方法時(shí)進(jìn)行加鎖,最后一次 append 方法結(jié)束后進(jìn)行解鎖。
偏向鎖/輕量級(jí)鎖/重量級(jí)鎖
關(guān)于 synchronized 原理,在 從JMM 透析 volatile 與 synchronized 原理文中已經(jīng)詳細(xì)闡述。本文主要講解針對(duì) synchronized 性能低 JVM 所做的優(yōu)化手段。
偏向鎖
老王偏愛甄嬛,synchronized 大總管便在一個(gè)叫 Mark Word 里柜子存儲(chǔ)鎖偏向的線程ID,記錄著甄嬛的 ID,不需要執(zhí)行繁瑣的翻牌流程。只需要判斷下申請(qǐng)的王妃 ID 是否跟 柜子里記錄的 ID 一致。
因?yàn)橥跗珢壅鐙郑悦看我蚕矚g翻甄嬛的牌。
當(dāng)一個(gè)線程訪問同步代碼塊并獲取鎖時(shí),會(huì)在Mark Word里存儲(chǔ)鎖偏向的線程ID。在線程進(jìn)入和退出同步塊時(shí)不再通過CAS操作來(lái)加鎖和解鎖,而是檢測(cè) Mark Word 里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。
引入偏向鎖是為了在無(wú)多線程競(jìng)爭(zhēng)的情況下盡量減少不必要的輕量級(jí)鎖執(zhí)行路徑,因?yàn)檩p量級(jí)鎖的獲取及釋放依賴多次 CAS 原子指令,而偏向鎖只需要在置換ThreadID 的時(shí)候依賴一次 CAS 原子指令即可。
偏向鎖只有遇到其他線程嘗試競(jìng)爭(zhēng)偏向鎖時(shí),持有偏向鎖的線程才會(huì)釋放鎖,線程不會(huì)主動(dòng)釋放偏向鎖。偏向鎖的撤銷,需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒有字節(jié)碼正在執(zhí)行),它會(huì)首先暫停擁有偏向鎖的線程,判斷鎖對(duì)象是否處于被鎖定狀態(tài)。撤銷偏向鎖后恢復(fù)到無(wú)鎖(標(biāo)志位為“01”)或輕量級(jí)鎖(標(biāo)志位為“00”)的狀態(tài)。
偏向鎖在JDK 6及以后的 JVM 里是默認(rèn)啟用的。可以通過 JVM 參數(shù)關(guān)閉偏向鎖:-XX:-UseBiasedLocking=false,關(guān)閉之后程序默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài)。
輕量級(jí)鎖
是指當(dāng)鎖是偏向鎖的時(shí)候,被另外的線程所訪問,偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過自旋的形式嘗試獲取鎖,不會(huì)阻塞,從而提高性能。
在代碼進(jìn)入同步塊的時(shí)候,如果同步對(duì)象鎖狀態(tài)為無(wú)鎖狀態(tài)(鎖標(biāo)志位為“01”狀態(tài),是否為偏向鎖為“0”),虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個(gè)名為鎖記錄(Lock Record)的空間,用于存儲(chǔ)鎖對(duì)象目前的Mark Word的拷貝,官方稱之為 Displaced Mark Word。這時(shí)候線程堆棧與對(duì)象頭的狀態(tài)如下圖所示。
輕量級(jí)鎖
拷貝Object 對(duì)象頭中的 Mark Word復(fù)制到 LockRecord 中。
拷貝成功后,虛擬機(jī)將使用 CAS 操作嘗試將對(duì)象的 Mark Word 更新為指向 Lock Record 的指針,并將Lock record 里的 owne r指針指向 object mark word。如果更新成功,則執(zhí)行步驟 4。
如果這個(gè)更新動(dòng)作成功了,那么這個(gè)線程就擁有了該對(duì)象的鎖,并且對(duì)象Mark Word的鎖標(biāo)志位設(shè)置為“00”,即表示此對(duì)象處于輕量級(jí)鎖定狀態(tài),這時(shí)候線程堆棧與對(duì)象頭的狀態(tài)如下圖所示。
如果這個(gè)更新操作失敗了,虛擬機(jī)首先會(huì)檢查對(duì)象的Mark Word是否指向當(dāng)前線程的棧幀,如果是就說(shuō)明當(dāng)前線程已經(jīng)擁有了這個(gè)對(duì)象的鎖,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行。否則說(shuō)明多個(gè)線程競(jìng)爭(zhēng)鎖,若當(dāng)前只有一個(gè)等待線程,則可通過自旋稍微等待一下,可能另一個(gè)線程很快就會(huì)釋放鎖。但是當(dāng)自旋超過一定的次數(shù),或者一個(gè)線程在持有鎖,一個(gè)在自旋,又有第三個(gè)來(lái)訪時(shí),輕量級(jí)鎖膨脹為重量級(jí)鎖,重量級(jí)鎖使除了擁有鎖的線程以外的線程都阻塞,防止CPU空轉(zhuǎn),鎖標(biāo)志的狀態(tài)值變?yōu)?ldquo;10”,Mark Word中存儲(chǔ)的就是指向重量級(jí)鎖(互斥量)的指針,后面等待鎖的線程也要進(jìn)入阻塞狀態(tài)。
重量級(jí)鎖
如上輕量級(jí)鎖的加鎖過程步驟(5),輕量級(jí)鎖所適應(yīng)的場(chǎng)景是線程近乎交替執(zhí)行同步塊的情況,如果存在同一時(shí)間訪問同一鎖的情況,就會(huì)導(dǎo)致輕量級(jí)鎖膨脹為重量級(jí)鎖。Mark Word的鎖標(biāo)記位更新為10,Mark Word指向互斥量(重量級(jí)鎖)
Synchronized 的重量級(jí)鎖是通過對(duì)象內(nèi)部的一個(gè)叫做監(jiān)視器鎖(monitor)來(lái)實(shí)現(xiàn)的,監(jiān)視器鎖本質(zhì)又是依賴于底層的操作系統(tǒng)的 Mutex Lock(互斥鎖)來(lái)實(shí)現(xiàn)的。而操作系統(tǒng)實(shí)現(xiàn)線程之間的切換需要從用戶態(tài)轉(zhuǎn)換到核心態(tài),這個(gè)成本非常高,狀態(tài)之間的轉(zhuǎn)換需要相對(duì)比較長(zhǎng)的時(shí)間,這就是為什么 Synchronized 效率低的原因。
鎖升級(jí)路徑
從無(wú)鎖到偏向鎖,再到輕量級(jí)鎖,最后到重量級(jí)鎖。結(jié)合前面我們講過的知識(shí),偏向鎖性能最好,避免了 CAS 操作。而輕量級(jí)鎖利用自旋和 CAS 避免了重量級(jí)鎖帶來(lái)的線程阻塞和喚醒,性能中等。重量級(jí)鎖則會(huì)把獲取不到鎖的線程阻塞,性能最差。
鎖升級(jí)
綜上,偏向鎖通過對(duì)比 Mark Word 解決加鎖問題,避免執(zhí)行CAS操作。而輕量級(jí)鎖是通過用CAS操作和自旋來(lái)解決加鎖問題,避免線程阻塞和喚醒而影響性能。重量級(jí)鎖是將除了擁有鎖的線程以外的線程都阻塞。
本文轉(zhuǎn)載自微信公眾號(hào)「碼哥字節(jié) 」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系碼哥字節(jié)公眾號(hào)。