欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java必會(huì)的Synchronized底層原理剖析

 更新時(shí)間:2022年10月19日 15:33:50   作者:一燈架構(gòu)  
synchronized作為Java程序員最常用同步工具,很多人卻對(duì)它的用法和實(shí)現(xiàn)原理一知半解,以至于還有不少人認(rèn)為synchronized是重量級(jí)鎖,性能較差,盡量少用。但不可否認(rèn)的是synchronized依然是并發(fā)首選工具,本文就來(lái)詳細(xì)講講

synchronized作為Java程序員最常用同步工具,很多人卻對(duì)它的用法和實(shí)現(xiàn)原理一知半解,以至于還有不少人認(rèn)為synchronized是重量級(jí)鎖,性能較差,盡量少用。

但不可否認(rèn)的是synchronized依然是并發(fā)首選工具,連volatile、CAS、ReentrantLock都無(wú)法動(dòng)搖synchronized的地位。synchronized是工作面試中的必備技能,今天就跟著一燈一塊深入剖析synchronized的底層原理。

1. synchronized作用

synchronized是Java提供一種隱式鎖,無(wú)需開(kāi)發(fā)者手動(dòng)加鎖釋放鎖。保證多線程并發(fā)情況下數(shù)據(jù)的安全性,實(shí)現(xiàn)了同一個(gè)時(shí)刻只有一個(gè)線程能訪問(wèn)資源,其他線程只能阻塞等待,簡(jiǎn)單說(shuō)就是互斥同步。

2. synchronized用法

先看一下synchronized有哪幾種用法?

使用位置被鎖對(duì)象示例代碼
實(shí)例方法實(shí)例對(duì)象public synchronized void method() {
……
}
靜態(tài)方法class類(lèi)public static synchronized void method() {
……
}
實(shí)例對(duì)象實(shí)例對(duì)象public void method() {
Object obj = new Object();
synchronized (obj) {
……
}
}
類(lèi)對(duì)象class類(lèi)public void method() {
synchronized (Demo.class) {
……
}
}
this關(guān)鍵字實(shí)例對(duì)象public void method() {
synchronized (this) {
……
}
}

可以看到被鎖對(duì)象只要有兩種,實(shí)例對(duì)象和class類(lèi)。

  • 由于靜態(tài)方法可以通過(guò)類(lèi)名直接訪問(wèn),所以它跟直接加鎖在class類(lèi)上是一樣的。
  • 當(dāng)在實(shí)例方法、實(shí)例對(duì)象、this關(guān)鍵字上面加鎖的時(shí)候,鎖定范圍都是當(dāng)前實(shí)例對(duì)象。
  • 實(shí)例對(duì)象上面的鎖和class類(lèi)上面的鎖,兩者不互斥。

3. synchronized加鎖原理

當(dāng)我們使用synchronized在方法和對(duì)象上加鎖的時(shí)候,Java底層到底怎么實(shí)現(xiàn)加鎖的?

當(dāng)在類(lèi)對(duì)象上加鎖的時(shí)候,也就是在class類(lèi)加鎖,代碼如下:

/**
 * @author 一燈架構(gòu)
 * @apiNote Synchronized示例
 **/
public class SynchronizedDemo {

    public void method() {
        synchronized (SynchronizedDemo.class) {
            System.out.println("Hello world!");
        }
    }

}

反編譯一下,看一下源碼實(shí)現(xiàn):

可以看到,底層是通過(guò)monitorentermonitorexit兩個(gè)關(guān)鍵字實(shí)現(xiàn)的加鎖與釋放鎖,執(zhí)行同步代碼之前使用monitorenter加鎖,執(zhí)行完同步代碼使用monitorexit釋放鎖,拋出異常的時(shí)候也是用monitorexit釋放鎖。

寫(xiě)成偽代碼,類(lèi)似下面這樣:

/**
 * @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í)無(wú)論是ACC_SYNCHRONIZED關(guān)鍵字,還是monitorentermonitorexit,底層都是通過(guò)獲取monitor鎖來(lái)實(shí)現(xiàn)的加鎖與釋放鎖。

monitor鎖又是通過(guò)ObjectMonitor來(lái)實(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í)訪問(wèn)一段同步代碼時(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)搶鎖的過(guò)程要比上面展示得更加復(fù)雜。除了_EntryList 這個(gè)雙向鏈表用來(lái)保存競(jìng)爭(zhēng)的線程,ObjectMonitor中還有另外一個(gè)單向鏈表 _cxq,由兩個(gè)隊(duì)列來(lái)共同管理并發(fā)的線程。

以上就是Java必會(huì)的Synchronized底層原理剖析 的詳細(xì)內(nèi)容,更多關(guān)于Java Synchronized原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • IDEA maven compile報(bào)錯(cuò)OutOfMemoryError(內(nèi)存溢出)解決及jvm分析

    IDEA maven compile報(bào)錯(cuò)OutOfMemoryError(內(nèi)存溢出)解決及jvm分析

    遇到Maven編譯時(shí)報(bào)OutOfMemoryError錯(cuò)誤通常因?yàn)槟J(rèn)的堆內(nèi)存大小不足,本文就來(lái)介紹一下OutOfMemoryError(內(nèi)存溢出)解決,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-10-10
  • Java Spring JdbcTemplate基本使用詳解

    Java Spring JdbcTemplate基本使用詳解

    JDBC已經(jīng)能夠滿足大部分用戶最基本的需求,但是在使用JDBC時(shí),必須自己來(lái)管理數(shù)據(jù)庫(kù)資源如:獲取PreparedStatement,設(shè)置SQL語(yǔ)句參數(shù),關(guān)閉連接等步驟
    2021-10-10
  • Spring中過(guò)濾器(Filter)和攔截器(Interceptor)的區(qū)別和聯(lián)系解析

    Spring中過(guò)濾器(Filter)和攔截器(Interceptor)的區(qū)別和聯(lián)系解析

    在我們?nèi)粘5拈_(kāi)發(fā)中,我們經(jīng)常會(huì)用到Filter和Interceptor,這篇文章主要介紹了Spring中過(guò)濾器(Filter)和攔截器(Interceptor)的區(qū)別和聯(lián)系?,需要的朋友可以參考下
    2022-10-10
  • JVM 方法調(diào)用之動(dòng)態(tài)分派(詳解)

    JVM 方法調(diào)用之動(dòng)態(tài)分派(詳解)

    下面小編就為大家?guī)?lái)一篇JVM 方法調(diào)用之動(dòng)態(tài)分派(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • Java基礎(chǔ)學(xué)習(xí)之集合底層原理

    Java基礎(chǔ)學(xué)習(xí)之集合底層原理

    今天帶大家回顧Java基礎(chǔ)的相關(guān)知識(shí),文中對(duì)集合底層原理作了非常詳細(xì)的圖文介紹,對(duì)Java初學(xué)者有非常好的幫助,需要的朋友可以參考下
    2021-05-05
  • 詳解Java 集合類(lèi) List 的那些坑

    詳解Java 集合類(lèi) List 的那些坑

    這篇文章主要介紹了Java 集合類(lèi) List 的那些坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • java模擬微信搶紅包的實(shí)例代碼

    java模擬微信搶紅包的實(shí)例代碼

    現(xiàn)在搶紅包的功能很受歡迎,本篇文章主要介紹了java模擬微信搶紅包的實(shí)例代碼。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-03-03
  • Scala中優(yōu)雅的處理Null問(wèn)題

    Scala中優(yōu)雅的處理Null問(wèn)題

    Spark 采用混合方式,大部分情況下使用 Option,但個(gè)別時(shí)候出于性能原因才使用了null。一個(gè)很好的習(xí)慣是當(dāng)有方法返回值可能為null的時(shí)候,使用Option來(lái)代替,本文給大家介紹Scala處理Null的知識(shí)詳解,一起看看吧
    2021-08-08
  • Java圖片讀取ImageIO.read()報(bào)錯(cuò)問(wèn)題及解決

    Java圖片讀取ImageIO.read()報(bào)錯(cuò)問(wèn)題及解決

    在使用imageio庫(kù)讀取圖片時(shí),如果路徑中包含中文,可能會(huì)導(dǎo)致讀取失敗,解決方法是將路徑中的中文字符進(jìn)行轉(zhuǎn)義處理,可以使用ImageUtil.java工具類(lèi)進(jìn)行路徑轉(zhuǎn)義,從而避免錯(cuò)誤,這是一個(gè)常見(jiàn)問(wèn)題,希望本文的解決方案能幫助到遇到相同問(wèn)題的開(kāi)發(fā)者
    2024-10-10
  • 關(guān)于spring的自定義緩存注解分析

    關(guān)于spring的自定義緩存注解分析

    這篇文章主要介紹了關(guān)于spring的自定義緩存注解分析,因?yàn)樗械膋ey的失效時(shí)間都一樣,要想實(shí)現(xiàn)不同的key不同的失效時(shí)間,就得需要自定義緩存注解,需要的朋友可以參考下
    2023-05-05

最新評(píng)論