https://www.cnblogs.com/dsj2016/p/5714921.htmlhtml
https://www.zhihu.com/question/55075763程序員
優化包括自旋鎖、鎖粗化、鎖消除(鎖清除)、輕量級鎖和偏向鎖。安全
這些都是Java虛擬機層面的優化,對Java程序員是無感知的(Java程序員在程序層面是看不出什麼的,可是JVM內部已對synchronized作了優化)。要說有感知的話,就是synchronized在多線程下的性能提升了(JDK1.6相比於JDK1.5),這些都得益於JVM層面鎖的優化,就是上面所說的自旋鎖、鎖粗化、鎖清除、偏向鎖和輕量級鎖。多線程
一般狀況下,共享數據的鎖定狀態只持續很短的一段時間,爲了這很短的一段時間進行上下文切換並不值得。性能
自旋就是不停循環看是否能等到上個線程本身釋放鎖。這個問題是基於一個現實考量的:不少拿了鎖的線程會很快釋放鎖。由於通常敏感的操做不會不少。固然這個是一個不能徹底肯定的狀況,只能說整體上是一種優化。優化
舉個例子就比如一我的要上廁所發現廁所裏面有人,他能夠:1,等一小會。2,跑去另外的地方上廁所。等一小會不必定能等到前一我的出來,不過若是跑去別的廁所的花費的時間確定比等一小會結果前一我的出來了長。固然等完告終果那我的沒出來仍是要跑去別的地方上廁所這是最慢的。spa
自適應自旋能夠根據以往自旋等待時間的經驗,計算出一個較爲合理的本次自旋等待時間。操作系統
好比第一次設置最多自旋10次,結果在自旋的過程當中成功得到了鎖,那麼下一次就能夠設置成最多自旋20次。道理是:一個鎖若是可以在自旋的過程當中被釋放說明頗有可能下一次也會發生這種事。那麼就更要給這個鎖某種「便利」方便其不阻塞得鎖(畢竟快了不少)。一樣若是屢次嘗試的結果是徹底不能自旋等到其釋放鎖,那麼就說明頗有可能這個臨界區裏面的操做比較耗時間。就減少自旋的次數,由於其可能性過小了。線程
如有一系列操做,反覆地對同一把鎖進行上鎖和解鎖操做,編譯器會擴大這部分代碼的同步塊的邊界,從而只使用一次上鎖和解鎖操做。設計
試想有一個循環,循環裏面是一些敏感操做,有的人就在循環裏面寫上了synchronized關鍵字。這樣確實沒錯不過效率也許會很低,由於其頻繁地拿鎖釋放鎖。要知道鎖的取得(假如只考慮重量級MutexLock)是須要操做系統調用的,從用戶態進入內核態,開銷很大。因而針對這種狀況也許虛擬機發現了以後會適當擴大加鎖的範圍(因此叫鎖粗化)以免頻繁的拿鎖釋放鎖的過程。
編譯器會清除一些使用了同步,但同步塊中沒有涉及共享數據的鎖,從而減小多餘的同步。
經過逃逸分析發現其實根本就沒有別的線程產生競爭的可能(別的線程沒有臨界量的引用),而「自做多情」地給本身加上了鎖。有可能虛擬機會直接去掉這個鎖。
使用CAS取代互斥同步。
重量級鎖是一種悲觀鎖,使用互斥同步來保證線程的安全;
輕量級鎖是一種樂觀鎖,使用CAS操做進行同步。
當線程請求鎖時,若該鎖對象的Mark Word中標誌位爲01(未鎖定狀態),則在該線程的棧幀中建立一塊名爲『鎖記錄』的空間,而後將鎖對象的Mark Word拷貝至該空間;而後經過CAS操做將鎖對象的Mark Word指向該鎖記錄;
若CAS操做成功,則輕量級鎖的上鎖過程成功;
若CAS操做失敗,再判斷當前線程是否已經持有了該輕量級鎖;若已經持有,則直接進入同步塊;若還沒有持有,則表示該鎖已經被其餘線程佔用,此時輕量級鎖就要膨脹成重量級鎖。
輕量級鎖比重量級鎖性能更高的前提是,在輕量級鎖被佔用的整個同步週期內,不存在其餘線程的競爭。若在該過程當中一旦有其餘線程競爭,那麼就會膨脹成重量級鎖,從而除了使用互斥量之外,還額外發生了CAS操做,此時耗時更長,會更慢。
偏向鎖是爲了消除無競爭狀況下的同步原語,進一步提高程序性能。
輕量級鎖是在無競爭的狀況下使用CAS操做來代替互斥量的使用,從而實現同步;而偏向鎖是在無競爭的狀況下徹底取消同步。
它們都是樂觀鎖,都認爲同步期間不會有其餘線程競爭鎖。
當線程請求到鎖對象後,將鎖對象的狀態標誌位改成01,即偏向模式。而後使用CAS操做將線程的ID記錄在鎖對象的Mark Word中。之後該線程能夠直接進入同步塊,連CAS操做都不須要。可是,一旦有第二條線程須要競爭鎖,那麼偏向模式當即結束,進入輕量級鎖的狀態。
偏向鎖能夠提升有同步但沒有競爭的程序性能。可是若是鎖對象時常被多條線程競爭,那偏向鎖就是多餘的。