通過(guò)本文檔你將學(xué)習(xí)到
- 輕量級(jí)鎖
- 鎖膨脹
- 自選優(yōu)化
- 偏向鎖
- 鎖消除
接上一話,我們想個(gè)辦法布蔗,不能一說(shuō)送快遞,你直接選擇東風(fēng)快遞是吧浪腐,一加鎖你直接使用重量級(jí)鎖纵揍。我們想個(gè)辦法,有時(shí)候沒(méi)必要直接用東風(fēng)快遞议街,可以用跑腿對(duì)吧泽谨。那下面就開(kāi)始介紹下:鎖的優(yōu)化過(guò)程
1、前置知識(shí)
《深入理解java虛擬機(jī)》的書(shū)中知道特漩,我們可以通過(guò)分析Java對(duì)象頭中MarkWord來(lái)查看是那種鎖吧雹。
Java的對(duì)象頭在對(duì)象的不同的狀態(tài)下會(huì)有不同的表現(xiàn)形式,主要有三種狀態(tài)涂身,無(wú)鎖狀態(tài)雄卷,加鎖狀態(tài),GC標(biāo)記狀態(tài)蛤售。那么就可以理解Java當(dāng)中的上鎖其實(shí)可以理解給對(duì)象上鎖丁鹉。也就是改變對(duì)象頭的狀態(tài),如果上鎖成功則進(jìn)入同步代碼塊悴能。但是Java當(dāng)中的鎖又分為很多種揣钦,從上圖可以看出大體分為偏向鎖、輕量鎖搜骡、重量鎖三種鎖狀態(tài)。這三種鎖的效率是完全不同
關(guān)于對(duì)象狀態(tài)一共分為五種狀態(tài)佑女,分別是無(wú)鎖记靡、偏向鎖、輕量鎖团驱、重量鎖摸吠、GC標(biāo)記,但是2bit只能表示4種狀態(tài)(00,01,10,11)JVM的做法將偏向鎖和無(wú)鎖的狀態(tài)表示為同一個(gè)狀態(tài)嚎花,然后根據(jù)圖中偏向鎖的標(biāo)識(shí)再去標(biāo)識(shí)是無(wú)鎖還是偏向鎖狀態(tài)寸痢。
01 無(wú)鎖 (010)
01偏向鎖(101)
00輕量級(jí)鎖
10重量級(jí)鎖
這四個(gè)鎖是什么意思,MarkWord什么鬼紊选,MarkWord就是一個(gè)對(duì)象的一部分啼止,里面存了一些信息道逗,鎖的問(wèn)題下面會(huì)慢慢介紹
Java的對(duì)象頭存儲(chǔ)的是什么?我們可以看下
假設(shè)我們理解一個(gè)對(duì)象頭主要由上圖兩個(gè)部分組成(數(shù)組對(duì)象除外献烦,數(shù)組對(duì)象的對(duì)象還包含一個(gè)數(shù)組長(zhǎng)度)
2滓窍、輕量級(jí)鎖
輕量級(jí)鎖的使用場(chǎng)景:如果一個(gè)對(duì)象雖然有多線程要加鎖,但加鎖的時(shí)間是錯(cuò)開(kāi)的(也就是沒(méi)有競(jìng)爭(zhēng))巩那,那么可以使用輕量級(jí)鎖來(lái)優(yōu)化吏夯。
輕量級(jí)鎖對(duì)使用者是透明的,即語(yǔ)法仍然是 synchronized
假設(shè)有兩個(gè)方法同步塊即横,利用同一個(gè)對(duì)象加鎖
static final Object obj = new Object();
public static void method1() {
synchronized( obj ) {
// 同步塊 A
method2();
}
}
public static void method2() {
synchronized( obj ) {
// 同步塊 B
}
}
創(chuàng)建鎖記錄(Lock Record)對(duì)象噪生,每個(gè)線程都的棧幀都會(huì)包含一個(gè)鎖記錄的結(jié)構(gòu),內(nèi)部可以存儲(chǔ)鎖定對(duì)象的Mark Word
讓鎖記錄中 Object reference 指向鎖對(duì)象东囚,并嘗試用 cas 替換 Object 的 Mark Word跺嗽,將 Mark Word 的值存
入鎖記錄
如果 cas 替換成功,對(duì)象頭中存儲(chǔ)了 鎖記錄地址和狀態(tài) 00 舔庶,表示由該線程給對(duì)象加鎖抛蚁,這時(shí)圖示如下
如果 cas 失敗,有兩種情況
如果是其它線程已經(jīng)持有了該 Object 的輕量級(jí)鎖惕橙,這時(shí)表明有競(jìng)爭(zhēng)瞧甩,進(jìn)入鎖膨脹過(guò)程
如果是自己執(zhí)行了 synchronized 鎖重入,那么再添加一條 Lock Record 作為重入的計(jì)數(shù)
當(dāng)退出 synchronized 代碼塊(解鎖時(shí))如果有取值為 null 的鎖記錄弥鹦,表示有重入肚逸,這時(shí)重置鎖記錄,表示重入計(jì)數(shù)減一
當(dāng)退出 synchronized 代碼塊(解鎖時(shí))鎖記錄的值不為 null彬坏,這時(shí)使用 cas 將 Mark Word 的值恢復(fù)給對(duì)象
頭
成功朦促,則解鎖成功
失敗,說(shuō)明輕量級(jí)鎖進(jìn)行了鎖膨脹或已經(jīng)升級(jí)為重量級(jí)鎖栓始,進(jìn)入重量級(jí)鎖解鎖流程
2 鎖膨脹
如果在嘗試加輕量級(jí)鎖的過(guò)程中务冕,CAS 操作無(wú)法成功,這時(shí)一種情況就是有其它線程為此對(duì)象加上了輕量級(jí)鎖(有競(jìng)爭(zhēng))幻赚,這時(shí)需要進(jìn)行鎖膨脹禀忆,將輕量級(jí)鎖變?yōu)橹亓考?jí)鎖。
static Object obj = new Object();
public static void method1() {
synchronized( obj ) {
// 同步塊
}
}
當(dāng) Thread-1 進(jìn)行輕量級(jí)加鎖時(shí)落恼,Thread-0 已經(jīng)對(duì)該對(duì)象加了輕量級(jí)鎖
這時(shí) Thread-1 加輕量級(jí)鎖失敗箩退,進(jìn)入鎖膨脹流程
即為 Object 對(duì)象申請(qǐng) Monitor 鎖,讓 Object 指向重量級(jí)鎖地址
然后自己進(jìn)入 Monitor 的 EntryList BLOCKED
當(dāng) Thread-0 退出同步塊解鎖時(shí)佳谦,使用 cas 將 Mark Word 的值恢復(fù)給對(duì)象頭戴涝,失敗。這時(shí)會(huì)進(jìn)入重量級(jí)解鎖
流程,即按照 Monitor 地址找到 Monitor 對(duì)象啥刻,設(shè)置 Owner 為 null奸鸯,喚醒 EntryList 中 BLOCKED 線程