首先介紹關(guān)于synchronized的一些基礎(chǔ)知識(shí)邢羔。
作用于實(shí)例他去、方法、Class上的效果有什么不同
實(shí)例
Object lock = new Object();
synchronized (lock) {
doSomething();
}
synchronized作用于實(shí)例時(shí)柱锹,是怎么實(shí)現(xiàn)鎖的功能的呢哪自?
通過查看字節(jié)碼指令我們可以發(fā)現(xiàn)monitorenter
和monitorexit
兩個(gè)指令,這兩個(gè)指令就是通過Monitor來進(jìn)行獲取鎖釋放鎖的指令禁熏。
11: monitorenter
12: invokestatic #3 // Method doSomething:()V
15: aload_2
16: monitorexit
方法
public synchronized void doSomething(){
}
public synchronized void doSomething();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED //看這里
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 11: 0
通過字節(jié)碼可以看到壤巷,doSomething()方法在常量池中的method_info中被標(biāo)記為ACC_SYNCHRONIZED,當(dāng)線程訪問方法時(shí)會(huì)檢查是否存在該標(biāo)記瞧毙,如果存在胧华,則需要獲取Monitor對(duì)象,再通過Monitor的enter和exit來進(jìn)行獲取鎖釋放鎖的操作升筏。
Class
synchronized (Test.class) {
}
7: goto 15
10: astore_2
11: aload_1
12: monitorexit
根據(jù)字節(jié)碼指令可以看到撑柔,實(shí)現(xiàn)鎖的方式與作用于對(duì)象實(shí)例時(shí)相同,也是通過monitorenter
和monitorexit
兩個(gè)指令來實(shí)現(xiàn)的鎖您访。不同于實(shí)例的地方是铅忿,作用于Class時(shí),鎖的對(duì)象是Class對(duì)象的實(shí)例灵汪。
Monitor
前文中提到的
monitorenter
和monitorexit
檀训,以及Monitor
,都是什么呢享言?
Monitor是鎖的概念峻凫,在Hotspot虛擬機(jī)中它是由ObjectMonitor來實(shí)現(xiàn)的,我們的字節(jié)碼指令 monitorenter
和monitorexit
览露,也都是由該對(duì)象來實(shí)現(xiàn)荧琼。如果還有希望更深入研究的同學(xué),可以去翻閱Hotspot的源碼差牛,這部分在ObjectMonitor文件中可以找到命锄。
ObjectMonitor(){
_header = NULL;
_count = 0; //記錄個(gè)數(shù)
_waiters = 0;
_owner = NULL;
_WaitSet = NULL;//處于wait狀態(tài)的線程,會(huì)被加入到_WaitSet
_WaitSetLock = 0;
_EntryList = NULL;
...
...
}
這就是synchronized的全部嗎偏化?
當(dāng)然不是脐恩。
在以前的認(rèn)知中,我們認(rèn)為synchronized的性能會(huì)比Lock差很多侦讨,但是在jdk6之后驶冒,synchronized得到了一次性能優(yōu)化苟翻,這次性能優(yōu)化,就是鎖膨脹骗污。
鎖膨脹
鎖膨脹用一句話簡單概括就是崇猫,一個(gè)鎖經(jīng)過三次膨脹變的笨重的但穩(wěn)重過程。
換句話講身堡,每一個(gè)對(duì)象邓尤,都有四種鎖的狀態(tài)拍鲤。
由淺至深贴谎,由弱變強(qiáng)分為:
無鎖 > 偏向鎖 > 輕量級(jí)鎖 > 重量級(jí)鎖
這四個(gè)狀態(tài)是怎么區(qū)分的呢?
對(duì)象頭中的Mark word區(qū)域季稳,存儲(chǔ)著對(duì)象的哈希值擅这,GC年齡,鎖標(biāo)記位
景鼠,是否偏向仲翎,偏向線程
等數(shù)據(jù)。
鎖狀態(tài) | 是否偏向 | 鎖標(biāo)志位 |
---|---|---|
無鎖 | 0 | 00 |
偏向鎖 | 1 | 01 |
輕量級(jí)鎖 | 00 | |
重量級(jí)鎖 | 10 |
無鎖狀態(tài):當(dāng)對(duì)象從來沒有線程請求獲取鎖铛漓。
偏向鎖溯香,何為偏向,是指該實(shí)例將會(huì)偏向第一個(gè)來獲取它的鎖的線程浓恶,如果沒有其它線程來請求獲取鎖玫坛,則持有該鎖的線程永遠(yuǎn)不需要同步。
- 當(dāng)鎖第一次被線程獲取時(shí)包晰,通過CAS將線程ID記錄到對(duì)象頭的Mark word中湿镀,并且修改偏向標(biāo)志,之后該線程在進(jìn)入和退出同步塊時(shí)伐憾,不需要進(jìn)行獲取鎖和釋放鎖的操作勉痴,僅測試Mark word中的線程ID
- 當(dāng)另外線程嘗試獲取該鎖時(shí),偏向模式結(jié)束
- 新線程發(fā)現(xiàn)該對(duì)象的偏向狀態(tài)時(shí)树肃,表明已經(jīng)存在競爭
- 檢查該鎖偏向的線程是否存活
- 如果原線程掛掉的話則恢復(fù)到無鎖狀態(tài)并重新偏向到新的線程
- 如果原線程依然存活蒸矛,則檢查原線程的操作棧是否需要使用該鎖
- 如果原線程不需要使用該鎖,則對(duì)象狀態(tài)恢復(fù)到無鎖狀態(tài)并重新偏向到新的線程
- 如果原線程仍然需要使用該鎖胸嘴,則偏向鎖升級(jí)為輕量級(jí)鎖
輕量級(jí)鎖 雏掠,輕量級(jí)鎖是通過CAS來避免使用互斥量的開銷
- 當(dāng)獲取鎖的線程,檢查鎖狀態(tài)是否為無鎖(即:是否偏向->是筛谚,鎖標(biāo)志位->01偏向鎖)磁玉。
- 當(dāng)前線程將在當(dāng)前的棧幀中申請一塊空間,Lock Record(包含兩部分驾讲,一部分存放對(duì)象頭中的Mark Word的拷貝蚊伞,稱為Displaced Mark Word席赂,另一部分為指向owner)。
- 將對(duì)象頭中的Mark Word拷貝至Lock Record中时迫,并通過CAS操作將對(duì)象頭中Mark Word更新為指向Lock Record的指針颅停;將Lock Record中的owner通過CAS更新為指向?qū)ο箢^中Mark Word的指針
- 如果更新成功,則將對(duì)象頭中的鎖標(biāo)志位更新為00掠拳,即輕量級(jí)鎖
- 如果更新失敗癞揉,則檢查對(duì)象頭中的Mard Word是否指向當(dāng)前棧幀
- 如果是指向當(dāng)前棧幀,則表示已經(jīng)持有該鎖
- 如果不是溺欧,則代表產(chǎn)生競爭喊熟,膨脹為重量級(jí)鎖,更新鎖標(biāo)志位為10姐刁,Mark Word中存儲(chǔ)的就是指向重量級(jí)鎖的指針芥牌,后面等待鎖的線程也會(huì)進(jìn)入阻塞狀態(tài)