Sychronized原理
用法:
- 方法
- 代碼塊
在JDK 1.6之前,synchronized只有傳統的鎖機制,因此給開發者留下了synchronized關鍵字相比於其他同步機制性能不好的印象。在JDK 1.6引入了兩種新型鎖機制:偏向鎖和輕量級鎖,它們的引入是為了解決在沒有多線程競爭或基本沒有競爭的場景下因使用傳統鎖機制帶來的性能開銷問題。
鎖的升級: 偏向鎖->輕量級鎖->重量鎖
鎖的映射關系存在對象頭中的。32位系統上各狀態如圖所示:
偏向鎖:
當JVM啟用了偏向鎖,那么新創建的對象都是可偏向狀態,此時mark word里的thread id為0,表示未偏向任何線程
加鎖過程:
- 當對象第一次被線程獲取鎖時,發現是未偏向的,那就將thread id改為當前線程id,成功繼續執行同步塊中的代碼,失敗則升級為輕量級鎖
- 當被偏向的線程再次進入同步塊時,發現鎖偏向的就是當前線程,通過一些額外檢查后就繼續執行。
- 當其他線程進入同步塊,發現有偏向的線程了,會進入撤銷偏向鎖邏輯。
解鎖過程:
- 棧中的最近一條lock record的obj字段設置為null
輕量級鎖:
線程在執行同步塊之前,JVM會在線程的棧幀上建立一個Lock Record。其包括了一個存儲對象頭中的mark word的Displaced Mark Word以及一個對象頭指針。
加鎖過程:
- 在線程棧中創建一個Lock Record,將其obj refercence字段指向鎖對象。
- 通過CAS指令將Lock Record地址放在對象頭的mark word中,如果對象是無鎖狀態則修改成功,代表獲取到了輕量級鎖。如果失敗進入步驟3
- 如果線程以及持有該鎖了,代表這是鎖重入,設置Lock Record第一部分(Displaced Mark Word)為null,起到了一個重入計數器的作用。然后結束
- 走到這一步說明發生了競爭,膨脹為重量鎖。
解鎖過程:
- 遍歷線程棧,找到所有obj字段等於當前鎖對象的Lock Record
- 如果Lock Record的Displaced Mark Word為null,代表是一次重入,將obj設為null后continue
- 如果Lock Record的Displaced Mark Word不為null,則利用CAS指令將對象頭的mark word恢復成為Displaced Mark Word。如果成功,則continue,否則膨脹為重量級鎖
重量級鎖:
利用的是JVM的監視器(Monitor)
java會為每個object對象分配一個monitor,當某個對象的同步方法(synchronized methods )被多個線程調用時,該對象的monitor將負責處理這些訪問的並發獨占要求。
1.當Sychronized修飾在代碼塊上的時候,使用的是monitorenter指令和monitorexit指令。
monitorenter
過程如下:
- 如果Monitor的進入數為0,則該線程進入Monitor,然后進入數+1,然后該線程即為Monitor的所有者
- 如果線程已經占有了Monitor只是重新進入,則進入數+1
- 如果其他線程占有了,則線程阻塞,直到Monitor的進入數為0,在嘗試獲取
monitorexit
過程如下:
- 指令執行時,Monitor的進入數減一,如果進入數為0,則線程退出Monitor
- 其他被阻塞的線程可以嘗試獲取這個Monitor的所有權
2.Synchronize作用在方式里時,會加上一個ACC_SYNCHRONIZED 標識。當有這個標識后,線程執行將先獲取Monitor,獲取成功才能執行方法體。