您好,登錄后才能下訂單哦!
今天小編給大家分享一下Java關鍵字synchronized原理與鎖的狀態實例分析的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
自旋鎖:是指當一個線程獲取鎖的時候,如果鎖已經被其它線程獲取,那么該線程將循環等待,然后不斷的判斷鎖是否能被成功獲取,直到獲取到鎖才會退出循環。
樂觀鎖:假定沒有沖突,在修改數據時如果發現數據和之前獲取的不一致,則讀最新數據,重試修改。
悲觀鎖:假定會發生并發沖突,同步所有對數據的相關操作,從讀數據就開始上鎖。
獨享鎖(寫):給資源加上寫鎖,線程可以修改資源,其它線程不能再加鎖(單寫)。
共享鎖(讀):給資源加上讀鎖后只能讀不能修改,其它線程也只能加讀鎖,不能加寫鎖(多度)。看成Semaphore(信號量)理解即可。
可重入鎖&不可重入鎖:線程拿到一把鎖之后,可以自由進入同一把鎖所同步的其它代碼。
公平鎖&非公平鎖:爭搶鎖的順序,如果是按先來后到,則為公平。即能保證搶鎖的順序和搶到鎖的順序一致則為公平鎖。
特性:可重入、獨享、悲觀鎖。
鎖相關的優化:
鎖消除 :開啟鎖消除的參數有 -XX:+DoEscapeAnalysis
、 -XX:+EliminateLocks
。
鎖粗化:JDK做了鎖粗化的優化,但我們自己可從代碼層面優化。
/** * 鎖消除示例,JIT即時編譯,進行了鎖消除 * @author 劉亞樓 * @date 2020/1/16 */ public class LockEliminationExample { /** * StringBuilder線程不安全,StringBuffer用了synchronized關鍵字,是線程安全的 * 針對下面這種單線程加鎖、解鎖操作,JIT會進行優化,進行鎖消除 */ public static void eliminateLock() { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("a"); stringBuffer.append("b"); stringBuffer.append("c"); stringBuffer.append("a"); stringBuffer.append("b"); stringBuffer.append("c"); stringBuffer.append("a"); stringBuffer.append("b"); stringBuffer.append("c"); } }
/** * 鎖粗化示例 * @author 劉亞樓 * @date 2020/1/16 */ public class LockCoarseningExample { /** * 針對下面這種無意義的加鎖操作,JIT會進行優化,對變量i的所有操作放到一個同步代碼塊里 */ public static void lockCoarsening() { int i = 0; synchronized (LockCoarseningExample.class) { i++; } synchronized (LockCoarseningExample.class) { i--; } synchronized (LockCoarseningExample.class) { i++; } synchronized (LockCoarseningExample.class) { i++; i--; i++; } } }
備注:鎖消除和鎖粗化的區別在于鎖消除是針對單個線程重復加解鎖做的優化,最終沒有鎖的存在。而鎖粗化不只是針對單線程,且最終還是有鎖的存在。
首先,對象在堆中由對象頭、實例數據和對齊填充組成。
對象頭包含兩部分信息,第一部分用于存儲對象自身的運行時數據,如哈希碼、GC分代年齡、鎖狀態標志、線程持有的鎖、偏向鎖id等,這部分數據官方稱為"Mark Word"。
對象頭的另一部分是類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。
synchronized實現的鎖是通過改變對象頭的"Mark Word"來實現的。
"Mard Word"在32位和64位的虛擬機(未開啟壓縮指針)中分別為32位和64位。32位虛擬機"Mark Word"如下:
無鎖變成輕量級鎖時,多個線程會讀取對象的對象頭的無鎖狀態mark word內容,然后進行cas
操作進行修改,預期值是無鎖狀態mark word內容,新值是輕量級鎖狀態mark word內容,若修改成功,Lock record address
指向成功獲取鎖的線程的Lock Record
。
演示流程如下:
由于未成功獲取鎖的線程會自旋,長時間自旋會消耗CPU資源,因此自旋到一定次數會進行鎖升級,由輕量級鎖轉變為重量級鎖。
重量級鎖是通過object monitor(對象監視器)實現的,對象監視器包括entryList(鎖池)、owner(持鎖者)、waitSet(等待集合)等。
升級為重量級鎖時對象頭mark word的內容是monitor address(對象監視器地址),指向對象監視器。
演示流程如下:
備注:搶鎖失敗線程會進入entryList(鎖池),在調用wait方法后,線程會進入waitSet(等待集合),waitSet中的線程被喚醒后會重新進入entryList。
加鎖之后不解鎖,針對單線程
所謂偏向就是偏心,單線程加鎖之后就不再解鎖,減少了加鎖→業務處理→釋放鎖→加鎖操作流程。
在JDK6以后,默認已經開啟了偏向鎖這個優化,通過JVM參數-XX:-UseBiasedLocking
來禁用偏向鎖,若偏向鎖開啟,只有一個線程搶鎖,可獲取到偏向鎖。
關于偏向鎖Mark Word內容如下:
偏向標記第一次有用,出現過爭用后就沒用了。
偏向鎖本質就是無鎖,如果沒有發生過任何多線程爭搶鎖的情況,JVM認為就是單線程,無需做同步。
備注:JVM為了少干活,同步在JVM底層是有很多操作來實現的,如果沒有爭用,就不需要去做同步操作。
如果未開啟偏向鎖,無鎖狀態會先升級為輕量級鎖,輕量級鎖自選到一定程度升級為重量級鎖。
如果開啟了偏向鎖,有兩種情況:
當鎖未被占用時,會升級為無鎖,無鎖再升級為輕量級鎖,再由輕量級鎖升級為重量級鎖。
當鎖被占用時,會升級為輕量級鎖,再由輕量級鎖升級到重量級鎖。
以上就是“Java關鍵字synchronized原理與鎖的狀態實例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。