一、Synchronized鎖四個階段概述
java中synchronize鎖分為以下四個階段:
- 無鎖
- 偏向鎖
- 輕量級鎖
- 重量級鎖
其中偏向鎖和輕量級鎖是從java1.6開始引入。各階段之間的切換,如下圖:
從圖中會發現,其實偏向鎖是可以變成無鎖的,這看似不符合我們認知中的鎖可以升級不可以降級。單這種降級的本質,其實是偏向鎖->偏向鎖的一個過程。
二、Synchronized的鎖升級機制
2.1、無鎖到偏向鎖:
我們知道,Synchronized修飾的方法被調用前,其對象初始狀態是處於無鎖狀態的,其鎖標記位為01,此時當線程a調用此方法時,會通過CAS自旋,替換mark words。
偏向鎖是默認開啟的,而且開始時間一般是比應用程序啟動慢幾秒,如果不想有這個延遲,那么可以使用-XX:BiasedLockingStartUpDelay=0;
如果不想要偏向鎖,那么可以通過-XX:-UseBiasedLocking = false來設置;
2.2、偏向鎖到偏向鎖:
由於偏向鎖線程1獲取鎖后,不會主動修改對象頭,所以哪怕此線程1實際已消亡,之前加鎖對象的對象頭還是保持偏向鎖狀態。這個時候線程2想要進入同步方法,他會去查看線程1是否還存活,如果已經消亡,則把對鎖定對象的對象頭恢復成無鎖,然后重復無鎖->偏向鎖的過程。
同時如果線程1未消亡,但是其棧幀信息中不在需要此持有這個鎖對象,也會進行一次偏向鎖->偏向鎖的過程。
2.3、偏向鎖到輕量級鎖:
2.2的情況中,如果線程2需要進入同步方法,線程1還持有這個對象,那么就會進入偏向鎖->輕量級鎖的過程。
此時線程2進行cas替換失敗,會修改對象頭,升級為輕量級鎖,同時開啟自旋,重復嘗試替換。
2.4、輕量級鎖到重量級鎖:
輕量級鎖替換失敗到達一定次數(默認為10)后,輕量級鎖升級為重量級鎖。
需要注意,如果線程2自旋期間,有線程3也需要訪問同步方法,則立刻由輕量級鎖膨脹為重量級鎖
java1.6中,引入了自適應自旋鎖,自適應意味着自旋 的次數不是固定不變的,而是根據前一次在同一個鎖上自 旋的時間以及鎖的擁有者的狀態來決定。 如果在同一個鎖對象上,自旋等待 剛剛成功獲得過鎖,並 且持有鎖的線程正在運行中,那么虛擬機就會認為這次自 旋也是很有可能再次成功,進而它將允許自旋等待持續相 對更長的時間。
關於重量級鎖,我們之前說過其本質就是操作對象內部的監視器(monitor),這一點我們會在下一篇文章中進行詳細分析。