Java多线程系列(六):深入详解Synchronized同步锁的底层实现
談到多線程就不得不談到Synchronized,很多同學(xué)只會使用,缺不是很明白整個Synchronized的底層實現(xiàn)原理,這也是面試經(jīng)常被問到的環(huán)節(jié),比如:
今天主要分享以上內(nèi)容,詳解synchronized的底層實現(xiàn),多線程相關(guān)的可以參考:
Java多線程系列教程:線程的五大狀態(tài),以及線程之間的通信與協(xié)作?
Java多線程系列教程:Java線程池的使用方式,核心運行原理、以及注意事項
最全Java鎖詳解:獨享鎖/共享鎖+公平鎖/非公平鎖+樂觀鎖/悲觀鎖?
Java多線程系列教程:4種常用Java線程鎖的特點,性能比較、使用場景
史上最強多線程面試44題和答案:線程鎖+線程池+線程同步等
Synchronized
synchronized 翻譯為中文的意思是同步,也稱之為”同步鎖“。
synchronized的作用是保證在同一時刻,?被修飾的代碼塊或方法只會有一個線程執(zhí)行,以達(dá)到保證并發(fā)安全的效果。
Synchronized的使用
1.synchronized的3種使用方式
- 修飾實例方法:作用于當(dāng)前實例加鎖
- 修飾靜態(tài)方法:作用于當(dāng)前類對象加鎖
- 修飾代碼塊:指定加鎖對象,對給定對象加鎖
2.synchronized的代碼范例
Synchronized的底層實現(xiàn)
synchronized的底層實現(xiàn)是完全依賴與JVM虛擬機的。
所以談synchronized的底層實現(xiàn),就不得不談數(shù)據(jù)在JVM內(nèi)存的存儲:Java對象頭,以及Monitor對象監(jiān)視器。
1.Java對象頭
在JVM虛擬機中,對象在內(nèi)存中的存儲布局,可以分為三個區(qū)域:
- 對象頭(Header)
- 實例數(shù)據(jù)(Instance Data)
- 對齊填充(Padding)
Java對象頭主要包括兩部分?jǐn)?shù)據(jù):
- 類型指針(Klass Pointer):是對象指向它的類元數(shù)據(jù)的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例;
- 標(biāo)記字段(Mark Word):用于存儲對象自身的運行時數(shù)據(jù),如哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程 ID、偏向時間戳等等,它是實現(xiàn)輕量級鎖和偏向鎖的關(guān)鍵.
2.Java鎖對象存儲位置
所以,很明顯synchronized使用的鎖對象是存儲在Java對象頭里的標(biāo)記字段里。
3.Monitor
synchronized的對象鎖,其指針指向的是一個monitor對象(由C++實現(xiàn))的起始地址。每個對象實例都會有一個 monitor。
Monitor描述為對象監(jiān)視器,可以類比為一個特殊的房間,這個房間中有一些被保護(hù)的數(shù)據(jù),Monitor保證每次只能有一個線程能進(jìn)入這個房間進(jìn)行訪問被保護(hù)的數(shù)據(jù),進(jìn)入房間即為持有Monitor,退出房間即為釋放Monitor。
使用syncrhoized加鎖的同步代碼塊在字節(jié)碼引擎中執(zhí)行時,主要就是通過鎖對象的monitor的取用與釋放來實現(xiàn)的。
4.線程狀態(tài)流轉(zhuǎn)在Monitor上體現(xiàn)
描述為對象監(jiān)視器,當(dāng)多個線程同時請求某個對象監(jiān)視器時,對象監(jiān)視器會設(shè)置幾種狀態(tài)用來區(qū)分請求的線程:
- Contention List:所有請求鎖的線程將被首先放置到該競爭隊列
- Entry List:Contention List中那些有資格成為候選人的線程被移到Entry List
- Wait Set:那些調(diào)用wait方法被阻塞的線程被放置到Wait Set
- OnDeck:任何時刻最多只能有一個線程正在競爭鎖,該線程稱為OnDeck
- Owner:獲得鎖的線程稱為Owner
- !Owner:釋放鎖的線程
下圖反映了個狀態(tài)轉(zhuǎn)換關(guān)系
Synchronized 鎖的升級順序
鎖解決了數(shù)據(jù)的安全性,但是同樣帶來了性能的下降。hotspot 虛擬機的作者經(jīng)過調(diào)查發(fā)現(xiàn),大部分情況下,加鎖的代碼不僅僅不存在多線程競爭,而且總是由同一個線程多次獲得。所以基于這樣一個概率。
synchronized 在JDK1.6 之后做了一些優(yōu)化,為了減少獲得鎖和釋放鎖來的性能開銷,引入了偏向鎖、輕量級鎖、自旋鎖、重量級鎖,鎖的狀態(tài)根據(jù)競爭激烈的程度從低到高不斷升級。
1.偏向鎖
偏向鎖是JDK6中引入的一項鎖優(yōu)化,大多數(shù)情況下,鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖。
偏向鎖會偏向于第一個獲得它的線程,如果在接下來的執(zhí)行過程中,該鎖沒有被其他的線程獲取,則持有偏向鎖的線程將永遠(yuǎn)不需要同步。
2.輕量級鎖
如果明顯存在其它線程申請鎖,那么偏向鎖將很快升級為輕量級鎖。
3.自旋鎖
自旋鎖原理非常簡單,如果持有鎖的線程能在很短時間內(nèi)釋放鎖資源,那么那些等待競爭鎖的線程就不需要做內(nèi)核態(tài)和用戶態(tài)之間的切換進(jìn)入阻塞掛起狀態(tài),它們只需要等一等(自旋),等持有鎖的線程釋放鎖后即可立即獲取鎖,這樣就避免用戶線程和內(nèi)核的切換的消耗。
4.重量級鎖
指的是原始的Synchronized的實現(xiàn),重量級鎖的特點:其他線程試圖獲取鎖時,都會被阻塞,只有持有鎖的線程釋放鎖之后才會喚醒這些線程。
偏向鎖、輕量級鎖、重量級鎖優(yōu)劣勢比較
你可能也喜歡:
總結(jié)
以上是生活随笔為你收集整理的Java多线程系列(六):深入详解Synchronized同步锁的底层实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 全链路压测自动化实践
- 下一篇: 最全Java锁详解:独享锁/共享锁+公平