首先把pom引入。為了打印對象頭

直接開搞,新建一個SyncDemo類,建立兩個變量,int a 與 float b
新建一個SyncTest類,整個main方法,里面打印一下SyncDemo對象頭,記得pom引入jol,不然ClassLayout方法沒有。
注意:偏向鎖會延遲開啟,也就是說main線程啟動之后要過一段時間偏向鎖才會開啟。
我們可以通過sleep讓線程睡眠一下,或者設(shè)置關(guān)閉延遲(-XX:BiasedLockingStartupDelay=0)
睡眠一下,讓偏向鎖啟動。這時候看到狀態(tài)為101。
下面中途插播一個廣告,從別處而來,正規(guī)途徑:
這個圖可能大家都眼熟,來自jdk,上面說明了對象頭存儲了些啥
看圖上文字。其中可以看到int 和float我們沒有給他們賦予值,但是他們的值為0,因為會給他們賦予初始值。
對象頭:mark word 8字節(jié),class point 4字節(jié)
圈起來的就屬于對象頭中的Class內(nèi)存地址(指針)
001表示無鎖狀態(tài),計算了HashCode之后就無法使用偏向鎖,因為頭部存儲了HashCode,無法存儲線程ID。
十六進(jìn)制和存儲的位置是不是很像?
十三:哪里像了?
稍等,姿勢不對,十六進(jìn)制調(diào)轉(zhuǎn)一下,7d f2 16 8,這不是很像,是一模一樣
十三:為啥會導(dǎo)致這個玩意呢。
因為是高字節(jié)存在高地址中(小端存儲),所以我們看的時候要倒過來看,就如上圖前8個字節(jié)要倒過來看,01 7d f2 16 08 00 00 00-->00 00 00 08 16 f2 7d 01,現(xiàn)在你在來看,就等于我們打印的十六進(jìn)制?!緮?shù)據(jù)在內(nèi)存中存儲分為大小端模式,小端:高字節(jié)保存在內(nèi)存的高地址中. 大端:高字節(jié)保存在內(nèi)存的地地址中,具體可以百度一波:大小端模式】
廣告插播結(jié)束,回歸到代碼。
我們給他上鎖,看下對象頭中有什么變化。
101為偏向鎖,SyncDemo的對象頭存儲了偏向的線程ID。
這個時候疑問就來了,為什么上面同樣是101,但是頭部對象的值不一樣?不是說對象頭都會存儲線程id的嗎?為什么上面那一幅圖沒有呢。
注意:到底是不是偏向鎖,不僅要看101,還要看對象頭中有沒有存儲偏向線程ID,
存儲了線程ID則為偏向鎖(已經(jīng)偏向),沒有存儲線程ID,則為無鎖。
調(diào)用SyncDemo.hashCode之后,因為對象頭存儲了hashCode導(dǎo)致狀態(tài)變成了00,00是輕量鎖
我們再搞一個線程,進(jìn)行加鎖。
可以看到第一個打印的是偏向鎖,第二個就是變成輕量鎖了00。
繼續(xù)搞一個線程,現(xiàn)在是兩個線程了。
這個時候狀態(tài)就變成了10了,這是一把重量鎖,重量鎖是非常耗性能的。
我們來看一波class里面有啥,javap -v SyncTest.class命令,可以查看到
可以看到synchronized加鎖和解鎖都是自己進(jìn)行處理的,賊簡單只要一個synchronized,其它都不用做了,所以導(dǎo)致無腦加synchronized,這樣或許會導(dǎo)致性能問題(重量鎖),要根據(jù)問題對癥下藥。
總結(jié):
鎖的類型:
1、偏向鎖
對象剛開始是無鎖,經(jīng)過synchronized之后是偏向鎖,頭部存儲著線程ID。
2、輕量鎖
synchronized方法之后,其它線程也調(diào)用synchronized,此時鎖變成了輕量鎖。這個時候頭部存儲的值是棧中鎖記錄的指針。
3、重量鎖
調(diào)用synchronized方法之后,此時是偏向鎖,這個時候有線程并發(fā)執(zhí)行synchronized,競爭鎖資源,這個時候會升級成重量鎖。這個時候就很耗性能了,因為要調(diào)用系統(tǒng)內(nèi)核進(jìn)行線程互斥。