一、鎖的粗化
看如下程式碼
public class Test { StringBuffer stb = new StringBuffer(); public void test1(){ //jvm的優化,鎖的粗化 stb.append("1"); stb.append("2"); stb.append("3"); stb.append("4"); }
首先我們要清除StringBuffer是執行緒安全的,因為它在每一個方法上都加了synchronized鎖,下圖是StringBuffer的原始碼
按照正常的理解synchronized是對當前物件加鎖,那麼我們呼叫了四次append方法,那麼jvm是將這把物件鎖加了四次嗎?如下圖:
那這樣的化,jvm就需要加四次鎖,當然也要釋放四次鎖,頻繁加解鎖引起執行緒上下文的切換,非常消耗效能,所以jvm做了優化,只加一次鎖,叫做鎖的粗化,可以理解為將鎖的顆粒度放大
二、鎖的消除
如圖看下面程式碼
public void test2(){ //jvm的優化,JVM不會對同步塊進行加鎖 synchronized (new Object()) { //虛擬碼:很多邏輯 //jvm是否會加鎖? //jvm會進行逃逸分析 } }
這個地方加鎖等於沒有加鎖,因為每個執行緒都會new object,大家都不會用同一把鎖,jvm分析優化後不會對這種程式碼加鎖(逃逸分析),所以,我們平時加鎖一定要注意,加鎖要加同一把鎖。
三、鎖的膨脹升級
1、鎖的升級
synchronized的鎖的狀態總共有四種,無鎖狀態、偏向鎖、輕量級鎖和重量級鎖。隨著鎖的競爭,鎖可以從偏向鎖升級到輕量級鎖,再升級的重量級鎖,但是鎖的升級是單向的,也就是說只能從低到高升級,鎖狀態的升級不可逆。
JDK1.6版本之後對synchronized的實現進行了各種優化,如自旋鎖、偏向鎖和輕量級鎖 並預設開啟偏向鎖 開啟偏向鎖:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0 關閉偏向鎖:-XX:-UseBiasedLocking。如果直接上來就是重量級鎖,那麼實在是太消耗資源了。
2、鎖的狀態記錄在哪裡
3、對鎖的理解:
4、鎖的膨脹升級過程
注意一下幾點:
執行緒1獲取輕量級鎖後會將Object Mark Word 複製自己的一份到自己的棧空間,然後在自己的棧空間開闢一個指標lockerecord 指向Object Mark Word,同時Object Mark Word也會指向lockerecord,當執行緒1執行完程式碼塊釋放輕量級鎖之後,發現Object Mark Word不在指向自己,說明當前鎖已經改為重量級鎖,那麼它會喚醒阻塞佇列中所有執行緒重新競爭鎖。
總結:偏向鎖,輕量級鎖都是基於Object Mark Word的標記實現,java儘可能避免使用重量級鎖。