最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java synchronized輕量級鎖的核心原理詳解

 更新時間:2022年03月01日 16:51:06   作者:小小茶花女  
這篇文章主要為大家詳細介紹了Java synchronized輕量級鎖的核心原理,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

問題:

什么是自旋鎖?

說一下 synchronized 底層實現(xiàn)原理?

多線程中 synchronized 鎖升級的原理是什么?

1. 輕量級鎖的原理

引入輕量級鎖的主要目的是在多線程競爭不激烈的情況下,通過CAS機制競爭鎖減少重量級鎖產(chǎn)生的性能損耗。重量級鎖使用了操作系統(tǒng)底層的互斥鎖(Mutex Lock),會導(dǎo)致線程在用戶態(tài)和核心態(tài)之間頻繁切換,從而帶來較大的性能損耗。

輕量級鎖的使用場景:如果一個對象雖然有多線程要加鎖,但加鎖的時間是錯開的(也就是沒有競爭),那么可以 使用輕量級鎖來優(yōu)化。

輕量鎖存在的目的是盡可能不動用操作系統(tǒng)層面的互斥鎖,因為其性能比較差。線程的阻塞和喚醒需要CPU從用戶態(tài)轉(zhuǎn)為核心態(tài),頻繁地阻塞和喚醒對CPU來說是一件負(fù)擔(dān)很重的工作。同時我們可以發(fā)現(xiàn),很多對象鎖的鎖定狀態(tài)只會持續(xù)很短的一段時間,例如整數(shù)的自加操作,在很短的時間內(nèi)阻塞并喚醒線程顯然不值得,為此引入了輕量級鎖。輕量級鎖是一種自旋鎖,因為JVM本身就是一個應(yīng)用,所以希望在應(yīng)用層面上通過自旋解決線程同步問題。

public class Main {
    static final Object obj = new Object();
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
           method1();
        });
        thread.start();
    }

    public static void method1() {
        synchronized( obj ) {
            // 同步塊 A
            method2();
        }
    }
    public static void method2() {
        synchronized( obj ) {
            // 同步塊 B
        }
    }
}

輕量級鎖的執(zhí)行過程:

在搶鎖線程進入臨界區(qū)之前,如果內(nèi)置鎖沒有被鎖定,JVM首先將在搶鎖線程的棧幀中建立一個鎖記錄(Lock Record),用于存儲對象Mark Word的拷貝,

然后搶鎖線程將使用CAS自旋操作,嘗試將內(nèi)置鎖對象頭的Mark Wordptr_to_lock_record(鎖記錄指針)更新為搶鎖線程棧幀中鎖記錄的地址,如果這個更新執(zhí)行成功了,這個線程就擁有了這個對象鎖。然后JVM將Mark Word中的lock標(biāo)記位改為00(輕量級鎖標(biāo)志),即表示該對象處于輕量級鎖狀態(tài)。搶鎖成功之后,JVM會將Mark Word中原來的鎖對象信息(如哈希碼等)保存在搶鎖線程鎖記錄的Displaced Mark Word(可以理解為放錯地方的Mark Word)字段中,再將搶鎖線程中鎖記錄的owner指針指向鎖對象。

鎖記錄是線程私有的,每個線程都有自己的一份鎖記錄,在創(chuàng)建完鎖記錄后,會將內(nèi)置鎖對象的Mark Word復(fù)制到鎖記錄的Displaced Mark Word字段。這是為什么呢?因為內(nèi)置鎖對象的MarkWord的結(jié)構(gòu)會有所變化,Mark Word將會出現(xiàn)一個指向鎖記錄的指針,而不再存著無鎖狀態(tài)下的鎖對象哈希碼等信息,所以必須將這些信息暫存起來,供后面在鎖釋放時使用。

(1) 在搶鎖線程進入臨界區(qū)之前,如果內(nèi)置鎖沒有被鎖定,JVM首先將在搶鎖線程的棧幀中建立一個鎖記錄(Lock Record),每個線程都的棧幀都會包含一個鎖記錄的結(jié)構(gòu),內(nèi)部可以存儲鎖定對象的mark word

在這里插入圖片描述

(2) 搶鎖線程將使用CAS自旋操作,嘗試將內(nèi)置鎖對象頭的mark word的ptr_to_lock_record(鎖記錄指針)更新為搶鎖線程棧幀中鎖記錄的地址,如果這個更新執(zhí)行成功了,這個線程就擁有了這個對象鎖。然后jvm將mark word中的lock標(biāo)記位改為00,即表示該對象處于輕量級鎖狀態(tài)。

搶鎖成功之后,jvm會將mark word中原來的鎖對象信息(如哈希碼等)保存在搶鎖線程鎖記錄的Displaced Mark Word字段中,再將搶鎖線程中鎖記錄的owner指針指向鎖對象。

64位的mark word結(jié)構(gòu)如表所示:

在這里插入圖片描述

在輕量級鎖搶占成功之后,鎖記錄和對象頭的狀態(tài)如圖所示:

在這里插入圖片描述

鎖記錄是線程私有的,每個線程都有自己的一份鎖記錄,在創(chuàng)建完鎖記錄后,會將內(nèi)置鎖對象的Mark Word復(fù)制到鎖記錄的Displaced Mark Word字段。這是為什么呢?因為內(nèi)置鎖對象的mark word的結(jié)構(gòu)會有所變化,mark word將會出現(xiàn)一個指向鎖記錄的指針,而不再存著無鎖狀態(tài)下的鎖對象哈希碼等信息,所以必須將這些信息暫存起來,供后面在鎖釋放時使用。

(3) 如果 cas 失敗,有兩種情況:

  • 如果是其它線程已經(jīng)持有了該 Object 的輕量級鎖,這時表明有競爭,進入鎖膨脹過程 ;
  • 如果是自己執(zhí)行了 synchronized 鎖重入,那么再添加一條 Lock Record 作為重入的計數(shù);

在這里插入圖片描述

(4) 當(dāng)退出 synchronized 代碼塊(解鎖時)如果有取值為 null 的鎖記錄,表示有重入,這時重置鎖記錄,表示重入計數(shù)減一

在這里插入圖片描述

(5) 當(dāng)退出 synchronized 代碼塊(解鎖時)鎖記錄的值不為 null,這時使用 cas 將 mark word的值恢復(fù)給對象頭

成功,則解鎖成功失敗,說明輕量級鎖進行了鎖膨脹或已經(jīng)升級為重量級鎖,進入重量級鎖解鎖流程

2. 輕量級鎖的分類

輕量級鎖主要有兩種:普通自旋鎖和自適應(yīng)自旋鎖。

1、普通自旋鎖

所謂普通自旋鎖,就是指當(dāng)有線程來競爭鎖時,搶鎖線程會在原地循環(huán)等待,而不是被阻塞,直到那個占有鎖的線程釋放鎖之后,這個搶鎖線程才可以獲得鎖。

說明:

鎖在原地循環(huán)等待的時候是會消耗CPU的,就相當(dāng)于在執(zhí)行一個什么也不干的空循環(huán)。所以輕量級鎖適用于臨界區(qū)代碼耗時很短的場景,這樣線程在原地等待很短的時間就能夠獲得鎖了。默認(rèn)情況下,自旋的次數(shù)為10次,用戶可以通過-XX:PreBlockSpin選項來進行更改。

2、自適應(yīng)自旋鎖

所謂自適應(yīng)自旋鎖,就是等待線程空循環(huán)的自旋次數(shù)并非是固定的,而是會動態(tài)地根據(jù)實際情況來改變自旋等待的次數(shù),自旋次數(shù)由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定。自適應(yīng)自旋鎖的大概原理是:

  • 如果搶鎖線程在同一個鎖對象上之前成功獲得過鎖,jvm就會認(rèn)為這次自旋很有可能再次成功,因此允許自旋等待持續(xù)相對更長的時間。
  • 如果對于某個鎖,搶鎖線程很少成功獲得過,那么jvm將可能減少自旋時間甚至省略自旋過程,以避免浪費處理器資源。

自適應(yīng)自旋解決的是“鎖競爭時間不確定”的問題。自適應(yīng)自旋假定不同線程持有同一個鎖對象的時間基本相當(dāng),競爭程度趨于穩(wěn)定??偟乃枷胧牵焊鶕?jù)上一次自旋的時間與結(jié)果調(diào)整下一次自旋的時間。

JDK 1.6的輕量級鎖使用的是普通自旋鎖,且需要使用-XX:+UseSpinning選項手工開啟。

JDK 1.7后,輕量級鎖使用自適應(yīng)自旋鎖,JVM啟動時自動開啟,且自旋時間由JVM自動控制。

輕量級鎖也被稱為非阻塞同步、樂觀鎖,因為這個過程并沒有把線程阻塞掛起,而是讓線程空循環(huán)等待。

3. 輕量級鎖的膨脹

輕量級鎖的問題在哪里呢?

雖然大部分臨界區(qū)代碼的執(zhí)行時間都是很短的,但是也會存在執(zhí)行得很慢的臨界區(qū)代碼。臨界區(qū)代碼執(zhí)行耗時較長,在其執(zhí)行期間,其他線程都在原地自旋等待,會空消耗CPU。因此,如果競爭這個同步鎖的線程很多,就會有多個線程在原地等待繼續(xù)空循環(huán)消耗CPU(空自旋),這會帶來很大的性能損耗。

輕量級鎖的本意是為了減少多線程進入操作系統(tǒng)底層的互斥鎖的概率,并不是要替代操作系統(tǒng)互斥鎖。所以,在爭用激烈的場景下,輕量級鎖會膨脹為基于操作系統(tǒng)內(nèi)核互斥鎖實現(xiàn)的重量級鎖。

如果在嘗試加輕量級鎖的過程中,CAS 操作無法成功,這時一種情況就是有其它線程為此對象加上了輕量級鎖(有 競爭),這時需要進行鎖膨脹,將輕量級鎖變?yōu)橹亓考夋i。

(1) 當(dāng) Thread-1 進行輕量級加鎖時,Thread-0 已經(jīng)對該對象加了輕量級鎖

在這里插入圖片描述

這時 Thread-1 加輕量級鎖失敗,進入鎖膨脹流程,即為鎖對象申請 Monitor 鎖,讓鎖對象指向重量級鎖地址,然后自己進入 Monitor 的 EntryList BLOCKED

在這里插入圖片描述

當(dāng) Thread-0 退出同步塊解鎖時,使用 cas 將mark word的值恢復(fù)給對象頭,失敗。這時會進入重量級解鎖 流程,即按照 Monitor 地址找到 Monitor 對象,設(shè)置 Owner 為 null,喚醒 EntryList 中 BLOCKED 線程。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容! 

相關(guān)文章

  • Java實現(xiàn)監(jiān)聽UDP協(xié)議的指定端口并收到數(shù)據(jù)按照十六進制輸出方式

    Java實現(xiàn)監(jiān)聽UDP協(xié)議的指定端口并收到數(shù)據(jù)按照十六進制輸出方式

    這篇文章主要介紹了Java實現(xiàn)監(jiān)聽UDP協(xié)議的指定端口并收到數(shù)據(jù)按照十六進制輸出方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Java縮略圖生成庫之Thumbnailator應(yīng)用說明

    Java縮略圖生成庫之Thumbnailator應(yīng)用說明

    Thumbnailator是一個為Java界面更流暢的縮略圖生成庫,從API提供現(xiàn)有的圖像文件和圖像對象的縮略圖中簡化了縮略過程,兩三行代碼就能夠從現(xiàn)有圖片生成縮略圖,使用起來非常方便,需要的朋友可以了解下
    2012-12-12
  • SpringBoot中@Conditional注解的介紹及實踐

    SpringBoot中@Conditional注解的介紹及實踐

    在 Spring Boot 中,@Conditional 注解用于實現(xiàn) 條件化 Bean 裝配,本文將詳細介紹 @Conditional 相關(guān)的注解,并結(jié)合實際應(yīng)用示例講解其使用方式,感興趣的小伙伴可以了解下
    2025-03-03
  • Java中BigDecimal類的使用詳解

    Java中BigDecimal類的使用詳解

    這篇文章主要介紹了Java中BigDecimal類的使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 微信js-sdk+JAVA實現(xiàn)“分享到朋友圈”和“發(fā)送給朋友”功能詳解

    微信js-sdk+JAVA實現(xiàn)“分享到朋友圈”和“發(fā)送給朋友”功能詳解

    本文分享了微信js-sdk+JAVA實現(xiàn)“分享到朋友圈”和“發(fā)送給朋友”功能介紹,大家可以參考一下
    2018-03-03
  • Spring Boot Rest控制器單元測試過程解析

    Spring Boot Rest控制器單元測試過程解析

    這篇文章主要介紹了Spring Boot Rest控制器單元測試過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • 一篇文章帶你理解Java的SPI機制(圖文并茂)

    一篇文章帶你理解Java的SPI機制(圖文并茂)

    本文詳細介紹了Java的SPI機制,包括其定義、用途和實現(xiàn)方式,SPI(ServiceProviderInterface)是一種服務(wù)發(fā)現(xiàn)機制,用于實現(xiàn)框架或庫的擴展點,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-10-10
  • Java內(nèi)存溢出案例模擬和原理分析過程

    Java內(nèi)存溢出案例模擬和原理分析過程

    這篇文章主要介紹了Java內(nèi)存溢出案例模擬和原理分析過程,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • spring中JdbcTemplate操作oracle的存儲過程實例代碼

    spring中JdbcTemplate操作oracle的存儲過程實例代碼

    JdbcTemplate是Spring對JDBC的封裝,目的是使JDBC更加易于使用,JdbcTemplate是Spring的一部分,下面這篇文章主要給大家介紹了關(guān)于spring中JdbcTemplate操作oracle的存儲過程的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • SpringSecurity微服務(wù)實戰(zhàn)之公共模塊詳解

    SpringSecurity微服務(wù)實戰(zhàn)之公共模塊詳解

    這篇文章主要為大家介紹了SpringSecurity微服務(wù)實戰(zhàn)之公共模塊詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08

最新評論

富顺县| 辽中县| 金湖县| 道真| 淮南市| 唐海县| 丘北县| 和平区| 天台县| 双峰县| 宾阳县| 衢州市| 威远县| 黄大仙区| 青海省| 将乐县| 崇左市| 屏山县| 岢岚县| 高碑店市| 金堂县| 玉树县| 舟曲县| 临澧县| 理塘县| 商南县| 于都县| 闻喜县| 东城区| 基隆市| 青铜峡市| 黔南| 山阴县| 霍城县| 武宁县| 邵东县| 马尔康县| 随州市| 延津县| 崇阳县| 台南县|