Java必會(huì)的Synchronized底層原理剖析
synchronized作為Java程序員最常用同步工具,很多人卻對(duì)它的用法和實(shí)現(xiàn)原理一知半解,以至于還有不少人認(rèn)為synchronized是重量級(jí)鎖,性能較差,盡量少用。
但不可否認(rèn)的是synchronized依然是并發(fā)首選工具,連volatile、CAS、ReentrantLock都無法動(dòng)搖synchronized的地位。synchronized是工作面試中的必備技能,今天就跟著一燈一塊深入剖析synchronized的底層原理。
1. synchronized作用
synchronized是Java提供一種隱式鎖,無需開發(fā)者手動(dòng)加鎖釋放鎖。保證多線程并發(fā)情況下數(shù)據(jù)的安全性,實(shí)現(xiàn)了同一個(gè)時(shí)刻只有一個(gè)線程能訪問資源,其他線程只能阻塞等待,簡(jiǎn)單說就是互斥同步。
2. synchronized用法
先看一下synchronized有哪幾種用法?
| 使用位置 | 被鎖對(duì)象 | 示例代碼 |
|---|---|---|
| 實(shí)例方法 | 實(shí)例對(duì)象 | public synchronized void method() { …… } |
| 靜態(tài)方法 | class類 | public static synchronized void method() { …… } |
| 實(shí)例對(duì)象 | 實(shí)例對(duì)象 | public void method() { Object obj = new Object(); synchronized (obj) { …… } } |
| 類對(duì)象 | class類 | public void method() { synchronized (Demo.class) { …… } } |
| this關(guān)鍵字 | 實(shí)例對(duì)象 | public void method() { synchronized (this) { …… } } |
可以看到被鎖對(duì)象只要有兩種,實(shí)例對(duì)象和class類。
- 由于靜態(tài)方法可以通過類名直接訪問,所以它跟直接加鎖在class類上是一樣的。
- 當(dāng)在實(shí)例方法、實(shí)例對(duì)象、this關(guān)鍵字上面加鎖的時(shí)候,鎖定范圍都是當(dāng)前實(shí)例對(duì)象。
- 實(shí)例對(duì)象上面的鎖和class類上面的鎖,兩者不互斥。
3. synchronized加鎖原理
當(dāng)我們使用synchronized在方法和對(duì)象上加鎖的時(shí)候,Java底層到底怎么實(shí)現(xiàn)加鎖的?
當(dāng)在類對(duì)象上加鎖的時(shí)候,也就是在class類加鎖,代碼如下:
/**
* @author 一燈架構(gòu)
* @apiNote Synchronized示例
**/
public class SynchronizedDemo {
public void method() {
synchronized (SynchronizedDemo.class) {
System.out.println("Hello world!");
}
}
}
反編譯一下,看一下源碼實(shí)現(xiàn):

可以看到,底層是通過monitorenter和monitorexit兩個(gè)關(guān)鍵字實(shí)現(xiàn)的加鎖與釋放鎖,執(zhí)行同步代碼之前使用monitorenter加鎖,執(zhí)行完同步代碼使用monitorexit釋放鎖,拋出異常的時(shí)候也是用monitorexit釋放鎖。
寫成偽代碼,類似下面這樣:
/**
* @author 一燈架構(gòu)
* @apiNote Synchronized示例
**/
public class SynchronizedDemo {
public void method() {
try {
monitorenter 加鎖;
System.out.println("Hello world!");
monitorexit 釋放鎖;
} catch (Exception e) {
monitorexit 釋放鎖;
}
}
}
當(dāng)在實(shí)例方法上加鎖,底層是怎么實(shí)現(xiàn)的呢?代碼如下:
/**
* @author 一燈架構(gòu)
* @apiNote Synchronized示例
**/
public class SynchronizedDemo {
public static synchronized void method() {
System.out.println("Hello world!");
}
}
再反編譯看一下底層實(shí)現(xiàn):

這次只使用了一個(gè)ACC_SYNCHRONIZED關(guān)鍵字,實(shí)現(xiàn)了隱式的加鎖與釋放鎖。其實(shí)無論是ACC_SYNCHRONIZED關(guān)鍵字,還是monitorenter和monitorexit,底層都是通過獲取monitor鎖來實(shí)現(xiàn)的加鎖與釋放鎖。
而monitor鎖又是通過ObjectMonitor來實(shí)現(xiàn)的,虛擬機(jī)中ObjectMonitor數(shù)據(jù)結(jié)構(gòu)如下(C++實(shí)現(xiàn)的):
ObjectMonitor() {
_header = NULL;
_count = 0; // WaitSet 和 EntryList 的節(jié)點(diǎn)數(shù)之和
_waiters = 0,
_recursions = 0; // 重入次數(shù)
_object = NULL;
_owner = NULL; // 持有鎖的線程
_WaitSet = NULL; // 處于wait狀態(tài)的線程,會(huì)被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ; // 多個(gè)線程爭(zhēng)搶鎖,會(huì)先存入這個(gè)單向鏈表
FreeNext = NULL ;
_EntryList = NULL ; // 處于等待鎖block狀態(tài)的線程,會(huì)被加入到該列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}

圖上展示了ObjectMonitor的基本工作機(jī)制:
- 當(dāng)多個(gè)線程同時(shí)訪問一段同步代碼時(shí),首先會(huì)進(jìn)入 _EntryList 隊(duì)列中等待。
- 當(dāng)某個(gè)線程獲取到對(duì)象的Monitor鎖后進(jìn)入臨界區(qū)域,并把Monitor中的 _owner 變量設(shè)置為當(dāng)前線程,同時(shí)Monitor中的計(jì)數(shù)器 _count 加1。即獲得對(duì)象鎖。
- 若持有Monitor的線程調(diào)用 wait() 方法,將釋放當(dāng)前持有的Monitor鎖,_owner變量恢復(fù)為null,_count減1,同時(shí)該線程進(jìn)入 _WaitSet 集合中等待被喚醒。
- 在_WaitSet 集合中的線程會(huì)被再次放到_EntryList 隊(duì)列中,重新競(jìng)爭(zhēng)獲取鎖。
- 若當(dāng)前線程執(zhí)行完畢也將釋放Monitor并復(fù)位變量的值,以便其他線程進(jìn)入獲取鎖。
線程爭(zhēng)搶鎖的過程要比上面展示得更加復(fù)雜。除了_EntryList 這個(gè)雙向鏈表用來保存競(jìng)爭(zhēng)的線程,ObjectMonitor中還有另外一個(gè)單向鏈表 _cxq,由兩個(gè)隊(duì)列來共同管理并發(fā)的線程。

以上就是Java必會(huì)的Synchronized底層原理剖析 的詳細(xì)內(nèi)容,更多關(guān)于Java Synchronized原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Java同步鎖Synchronized底層源碼和原理剖析(推薦)
- Java synchronized底層的實(shí)現(xiàn)原理
- java并發(fā)編程synchronized底層實(shí)現(xiàn)原理
- Java?synchronized底層實(shí)現(xiàn)原理以及鎖優(yōu)化
- Java并發(fā)編程深入理解之Synchronized的使用及底層原理詳解 下
- Java并發(fā)編程深入理解之Synchronized的使用及底層原理詳解 上
- Java 并發(fā)編程學(xué)習(xí)筆記之Synchronized底層優(yōu)化
- Java Synchronized字節(jié)碼層分析體驗(yàn)
相關(guān)文章
Java中的Comparable接口與Comparator接口區(qū)別解析
文章介紹了Java中的Comparable接口和Comparator接口,Comparable接口定義了一個(gè)compareTo方法,用于比較對(duì)象的順序,實(shí)現(xiàn)Comparable接口的類可以提供自然排序規(guī)則,詳細(xì)介紹了Java中的Comparable接口與Comparator接口區(qū)別,感興趣的朋友一起看看吧2025-02-02
Spring定時(shí)任務(wù)實(shí)現(xiàn)與配置(一)
這篇文章主要為大家詳細(xì)介紹了Spring定時(shí)任務(wù)的實(shí)現(xiàn)與配置第一篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
java連接hdfs ha和調(diào)用mapreduce jar示例
這篇文章主要介紹了Java API連接HDFS HA和調(diào)用MapReduce jar包,需要的朋友可以參考下2014-03-03
Java中IO流之字符流與字節(jié)流的轉(zhuǎn)換方式
在Java中,字節(jié)流與字符流是處理數(shù)據(jù)的兩種方式,字節(jié)流適用于處理各種數(shù)據(jù)類型,如圖片、音頻等非文本數(shù)據(jù),而字符流專門用于處理文本數(shù)據(jù),Java提供了InputStreamReader和OutputStreamWriter這兩個(gè)類來實(shí)現(xiàn)字節(jié)流向字符流的轉(zhuǎn)換2024-10-10
Java數(shù)據(jù)結(jié)構(gòu)最清晰圖解二叉樹前 中 后序遍歷
樹是一種重要的非線性數(shù)據(jù)結(jié)構(gòu),直觀地看,它是數(shù)據(jù)元素(在樹中稱為結(jié)點(diǎn))按分支關(guān)系組織起來的結(jié)構(gòu),很象自然界中的樹那樣。樹結(jié)構(gòu)在客觀世界中廣泛存在,如人類社會(huì)的族譜和各種社會(huì)組織機(jī)構(gòu)都可用樹形象表示2022-01-01

