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

Java Synchronized的偏向鎖詳細(xì)分析

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

上篇文章已經(jīng)對(duì)Synchronized關(guān)鍵字做了初步的介紹,從字節(jié)碼層面介紹了Synchronized關(guān)鍵字,最終字節(jié)碼層面就是monitorenter和monitorexit字節(jié)碼指令。并且拿Synchronized關(guān)鍵字和Java的JUC包下的ReentrantLock做了比較。Synchronized關(guān)鍵字的初體驗(yàn)-超鏈接地址

那么本篇文章將開(kāi)始深入解析Synchronized關(guān)鍵字的底層原理,也就是解析Hotspot虛擬機(jī)對(duì)monitorenter和monitorexit字節(jié)碼指令的實(shí)現(xiàn)原理。

理論知識(shí)

相信各位讀者在準(zhǔn)備面試中,都會(huì)背到關(guān)于Synchronized關(guān)鍵字的面試題,什么對(duì)象頭、鎖標(biāo)志位、偏向鎖、輕量級(jí)鎖、重量級(jí)鎖,鎖升級(jí)的過(guò)程等等面試題。而對(duì)于一些不僅僅只想漂浮于表面的讀者來(lái)說(shuō),去看Synchronized底層源碼,只能說(shuō)是一頭霧水。所以筆者有考慮這方面,所以理論知識(shí)(給臨時(shí)抱佛腳背理論的讀者)和底層源碼(給喜歡研究底層源碼的讀者)都會(huì)在這個(gè)系列中。

偏向鎖存在的意義:

先從字面意思來(lái)解釋?zhuān)蛴谀硞€(gè)線程,是不是可以理解為偏向的這個(gè)線程獲取鎖都很效率呢?那么為什么要存在偏向鎖呢?讀者需要明白,任何框架存在的意義不僅僅是為了某一部分場(chǎng)景,肯定需要適配大部分場(chǎng)景,而Synchronized關(guān)鍵字使用的場(chǎng)景可能并發(fā)高,可能并發(fā)低,可能幾乎不存在并發(fā),所以實(shí)現(xiàn)者需要幫用戶去適配不同的場(chǎng)景,達(dá)到效率最高化。而對(duì)于幾乎不存在并發(fā)的場(chǎng)景,是不是可以理解為幾乎只有一個(gè)線程拿到Synchronized鎖,所以就存在偏向鎖去優(yōu)化這種場(chǎng)景,不讓所有場(chǎng)景都去走很復(fù)雜的邏輯。

偏向鎖實(shí)現(xiàn)的流程:

  • 拿到鎖競(jìng)爭(zhēng)對(duì)象
  • 從當(dāng)前線程棧中獲取到一個(gè)沒(méi)有使用的BasicObjectLock(用于記錄鎖狀態(tài))
  • 查看當(dāng)前是否開(kāi)啟了偏向鎖模式
  • 查看當(dāng)前偏向鎖是否偏向的是當(dāng)前線程,如果偏向的是當(dāng)前線程,直接退出(可以理解成命中緩存)
  • 查看當(dāng)前是否已經(jīng)鎖升級(jí)了,并且嘗試撤銷(xiāo)偏向鎖(想象一下并發(fā)過(guò)程中,可能其他線程已經(jīng)完成了鎖對(duì)象的鎖升級(jí))
  • 當(dāng)前epoch是否發(fā)生了改變,如果發(fā)生了改變,當(dāng)前線程可以嘗試獲取偏向鎖,嘗試成功直接退出
  • 當(dāng)前是否是匿名偏向,或者已經(jīng)偏向于某個(gè)線程,但是不是當(dāng)前線程,此時(shí)可以嘗試獲取鎖,獲取成功直接退出
  • 如果不支持偏向鎖或者第5步的撤銷(xiāo)偏向鎖失敗了,此時(shí)嘗試膨脹成輕量級(jí)鎖,如果輕量級(jí)鎖膨脹失敗了就繼續(xù)往上鎖膨脹

流程圖如下(僅只有偏向鎖邏輯)

源碼論證

首先,我們先需要知道Synchronized底層源碼的入口在哪里,在字節(jié)碼層面表示為monitorenter和monitorexit字節(jié)碼指令,而我們知道JVM是負(fù)責(zé)執(zhí)行字節(jié)碼,最終轉(zhuǎn)換成不同CPU平臺(tái)的ISA指令集(也稱之為跨平臺(tái))。而JVM執(zhí)行字節(jié)碼分為

  • CPP解釋執(zhí)行
  • 模板解釋執(zhí)行(匯編)
  • JIT編譯執(zhí)行

一級(jí)一級(jí)的優(yōu)化,而最根本是CPP解釋執(zhí)行,后者都是基于CPP解釋執(zhí)行的不斷優(yōu)化,后者的難度極大,所以讀者弄明白CPP解釋執(zhí)行就即可。

在Hotspot源碼中,CPP解釋執(zhí)行的入口在bytecodeInterpreter.cpp文件(這里要注意,JDK1.8不同版本對(duì)synchronized關(guān)鍵字實(shí)現(xiàn)有區(qū)別,所以本文選的是jdk8u40版本,其他版本可能沒(méi)有偏向鎖等等邏輯)

首先,讀者明白,使用Synchronized關(guān)鍵字時(shí)需要一個(gè)鎖對(duì)象,而底層就是操作這個(gè)鎖對(duì)象的對(duì)象頭,所以我們先從markOop.hpp文件中找到對(duì)象頭的描述信息,是不是跟外面8股文描述的一模一樣呢??

對(duì)象頭熟悉以后,源碼中就是操作對(duì)象頭,不同的鎖狀態(tài)設(shè)置不同對(duì)象頭,用對(duì)象頭來(lái)表示不同的鎖狀態(tài),替換對(duì)象頭的原子性依靠CAS來(lái)保證。如果存在并發(fā),那么CAS競(jìng)爭(zhēng)失敗的線程就會(huì)往下走,一步一步的鎖升級(jí),反而如果沒(méi)有競(jìng)爭(zhēng)那就默認(rèn)使用偏向鎖。

下面是Hotspot中C++解釋器對(duì)于monitorenter字節(jié)碼指令的解釋執(zhí)行源碼(注釋特別詳細(xì))。

CASE(_monitorenter): {
        // 拿到鎖對(duì)象
        oop lockee = STACK_OBJECT(-1);
        // derefing's lockee ought to provoke implicit null check
        CHECK_NULL(lockee);
        // find a free monitor or one already allocated for this object
        // if we find a matching object then we need a new monitor
        // since this is recursive enter
        // 從當(dāng)前線程棧中找到一個(gè)沒(méi)有被使用的BasicObjectLock
        // 作用:用來(lái)記錄鎖狀態(tài)
        BasicObjectLock* limit = istate->monitor_base();
        BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
        BasicObjectLock* entry = NULL;
        while (most_recent != limit ) {
          if (most_recent->obj() == NULL) entry = most_recent;
          else if (most_recent->obj() == lockee) break;
          most_recent++;
        }
        if (entry != NULL) {
          // 搶坑,為什么這里不需要CAS,因?yàn)閷儆诰€程棧(線程變量),線程安全。
          entry->set_obj(lockee);
          int success = false;
          // 得到epoch的掩碼。
          uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
          // 得到當(dāng)前鎖對(duì)象的對(duì)象頭。
          markOop mark = lockee->mark();
          intptr_t hash = (intptr_t) markOopDesc::no_hash;
          // implies UseBiasedLocking
          // 當(dāng)前是偏向鎖模式,可以用JVM參數(shù)UseBiasedLocking控制
          if (mark->has_bias_pattern()) {
            uintptr_t thread_ident;
            uintptr_t anticipated_bias_locking_value;
            thread_ident = (uintptr_t)istate->thread();
            // lockee->klass()->prototype_header() 是否拿到對(duì)象的類(lèi)模板的頭部信息。
            // lockee->klass()->prototype_header() | thread_ident) 是類(lèi)模板頭部信息組合上線程id
            // mark 是當(dāng)前鎖對(duì)象的頭部信息。
            // markOopDesc::age_mask_in_place 是當(dāng)前對(duì)象的年齡信息。
            // 所以與年齡無(wú)關(guān)
            // 所以拿鎖對(duì)象的原型對(duì)象的對(duì)象頭控制
            // lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark 如果為0 代表當(dāng)前對(duì)象頭偏向鎖偏向了當(dāng)前線程
            anticipated_bias_locking_value =
              (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
              ~((uintptr_t) markOopDesc::age_mask_in_place);
            // 等于0代表當(dāng)前鎖對(duì)象頭部和類(lèi)模板頭部一樣。
            // 所以這是一次偏向鎖的命中。
            if  (anticipated_bias_locking_value == 0) {
              // already biased towards this thread, nothing to do
              if (PrintBiasedLockingStatistics) {
                (* BiasedLocking::biased_lock_entry_count_addr())++;
              }
              success = true;
            }
            // 當(dāng)前對(duì)象頭已經(jīng)膨脹成輕量級(jí)或者重量級(jí)鎖了。也即非偏向鎖。
            else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
              // try revoke bias
              // 嘗試撤銷(xiāo)偏向鎖
              markOop header = lockee->klass()->prototype_header();
              if (hash != markOopDesc::no_hash) {
                header = header->copy_set_hash(hash);
              }
              // CAS嘗試取消偏向
              if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
                if (PrintBiasedLockingStatistics)
                  (*BiasedLocking::revoked_lock_entry_count_addr())++;
              }
            }
            // 來(lái)到這里可能表示當(dāng)前偏向于其他線程。
            // 而epoch發(fā)生了變動(dòng),表示批量撤銷(xiāo)偏向鎖了。
            // 當(dāng)前線程可以嘗試爭(zhēng)搶一次偏向鎖,沒(méi)有成功就去鎖升級(jí)
            else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
              // try rebias
              // 嘗試重偏向
              markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
              if (hash != markOopDesc::no_hash) {
                new_header = new_header->copy_set_hash(hash);
              }
              // CAS競(jìng)爭(zhēng),重偏向。
              if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
                if (PrintBiasedLockingStatistics)
                  (* BiasedLocking::rebiased_lock_entry_count_addr())++;
              }
              // CAS失敗,鎖升級(jí)
              else {
                  // 鎖升級(jí)邏輯
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
              success = true;
            }
            // 來(lái)到這里表示,當(dāng)前是匿名偏向鎖(也即暫時(shí)還沒(méi)有線程占用)
            // 或者是已經(jīng)偏向了某個(gè)線程,所以這里CAS嘗試一次
            else {
              // try to bias towards thread in case object is anonymously biased
              markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
                                                              (uintptr_t)markOopDesc::age_mask_in_place |
                                                              epoch_mask_in_place));
              if (hash != markOopDesc::no_hash) {
                header = header->copy_set_hash(hash);
              }
              markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
              // debugging hint
              DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
              // 如果是匿名偏向,這個(gè)CAS就有可能成功
              // 如果是已經(jīng)偏向其他線程,這個(gè)CAS不能成功,直接往鎖升級(jí)走
              if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {
                if (PrintBiasedLockingStatistics)
                  (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
              }
              // cas失敗
              else {
                  // 鎖升級(jí)邏輯
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
              success = true;
            }
          }
          // traditional lightweight locking
          // case1:如果當(dāng)前已經(jīng)鎖升級(jí)了
          // case2:如果當(dāng)前不支持偏向鎖
          if (!success) {
            markOop displaced = lockee->mark()->set_unlocked();
            entry->lock()->set_displaced_header(displaced);
            bool call_vm = UseHeavyMonitors;
            // UseHeavyMonitors是JVM參數(shù),是否直接開(kāi)啟重量級(jí)鎖
            // 如果不直接開(kāi)啟,就CAS競(jìng)爭(zhēng)輕量級(jí)鎖,競(jìng)爭(zhēng)成功就直接返回
            if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
              // Is it simple recursive case?
              // CAS失敗可能是鎖重入,如果不是鎖重入,那么就是競(jìng)爭(zhēng)失敗要往鎖升級(jí)邏輯走了。
              if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
                  // 輕量級(jí)鎖的鎖重入
                entry->lock()->set_displaced_header(NULL);
              } else {
                  // 鎖升級(jí)邏輯
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
            }
          }
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
        } else {
          istate->set_msg(more_monitors);
          UPDATE_PC_AND_RETURN(0); // Re-execute
        }
      }

要明白偏向鎖對(duì)應(yīng)的對(duì)象頭的幾個(gè)部分的意義,然后帶入到源碼中就比較容易理解。

  • 線程對(duì)象:偏向于那個(gè)線程(當(dāng)沒(méi)有線程對(duì)象時(shí),就代表是匿名偏向,此時(shí)線程都可以去競(jìng)爭(zhēng))
  • epoch:是否發(fā)生了批量鎖撤銷(xiāo)(為什么要鎖撤銷(xiāo)?因?yàn)槠蜴i升級(jí)為輕量級(jí)鎖就需要撤銷(xiāo))
  • 偏向鎖標(biāo)志位:0表示無(wú)鎖,1表示偏向鎖(偏向鎖和無(wú)鎖的鎖標(biāo)志位都是01)
  • 鎖標(biāo)志位:表示不同鎖狀態(tài),偏向鎖表示為01(要注意無(wú)鎖也是表示為01,所以需要額外的偏向鎖標(biāo)志位來(lái)區(qū)分是無(wú)鎖還是偏向鎖)

總結(jié)

可能源碼部分一直是一個(gè)難點(diǎn),操作的內(nèi)容太多了,并且還是C++實(shí)現(xiàn)的。但是從對(duì)象頭的角度去分析理解還是很有幫助。

到此這篇關(guān)于Java Synchronized的偏向鎖詳細(xì)分析的文章就介紹到這了,更多相關(guān)Java Synchronized偏向鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • spring redis 如何實(shí)現(xiàn)模糊查找key

    spring redis 如何實(shí)現(xiàn)模糊查找key

    這篇文章主要介紹了spring redis 如何實(shí)現(xiàn)模糊查找key的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Spring?Cloud?Eureka?搭建?&?集群方式

    Spring?Cloud?Eureka?搭建?&?集群方式

    這篇文章主要介紹了Spring?Cloud?Eureka?搭建?&?集群方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringMVC和Ajax的交互詳解(手工處理)

    SpringMVC和Ajax的交互詳解(手工處理)

    Ajax即異步的?JavaScript和XML,是一種無(wú)需重新加載整個(gè)網(wǎng)頁(yè)的情況下,能夠更新部分模塊的網(wǎng)頁(yè)技術(shù),下面這篇文章主要給大家介紹了關(guān)于SpringMVC和Ajax交互的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • JavaEE Filter敏感詞過(guò)濾的方法實(shí)例詳解

    JavaEE Filter敏感詞過(guò)濾的方法實(shí)例詳解

    我們無(wú)論是在聊天還是在留言時(shí),都有一些信息不希望別人看到。那么如果過(guò)濾這些關(guān)鍵詞呢?下面小編給大家分享JavaEE Filter敏感詞過(guò)濾的方法實(shí)例詳解,感興趣的朋友一起學(xué)習(xí)吧
    2016-05-05
  • Spring整合mybatis實(shí)現(xiàn)過(guò)程詳解

    Spring整合mybatis實(shí)現(xiàn)過(guò)程詳解

    這篇文章主要介紹了Spring整合mybatis實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • mybatis中insert主鍵ID獲取和多參數(shù)傳遞的示例代碼

    mybatis中insert主鍵ID獲取和多參數(shù)傳遞的示例代碼

    這篇文章主要介紹了mybatis中insert主鍵ID獲取和多參數(shù)傳遞的示例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • 詳解Java ES多節(jié)點(diǎn)任務(wù)的高效分發(fā)與收集實(shí)現(xiàn)

    詳解Java ES多節(jié)點(diǎn)任務(wù)的高效分發(fā)與收集實(shí)現(xiàn)

    ElasticSearch 是一個(gè)高可用開(kāi)源全文檢索和分析組件。提供存儲(chǔ)服務(wù),搜索服務(wù),大數(shù)據(jù)準(zhǔn)實(shí)時(shí)分析等。一般用于提供一些提供復(fù)雜搜索的應(yīng)用
    2021-06-06
  • MybatisPlus多表查詢及分頁(yè)查詢完整代碼

    MybatisPlus多表查詢及分頁(yè)查詢完整代碼

    這篇文章主要介紹了MybatisPlus多表查詢及分頁(yè)查詢完整代碼,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • 當(dāng)事務(wù)Transactional遇見(jiàn)異步線程出現(xiàn)的坑及解決

    當(dāng)事務(wù)Transactional遇見(jiàn)異步線程出現(xiàn)的坑及解決

    這篇文章主要介紹了當(dāng)事務(wù)Transactional遇見(jiàn)異步線程出現(xiàn)的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java編程中的檢查型異常與非檢查型異常分析

    Java編程中的檢查型異常與非檢查型異常分析

    這篇文章主要介紹了Java編程中的未檢查型異常與非檢查型異常,以及異常的處理方式,需要的朋友可以參考下
    2017-09-09

最新評(píng)論

武胜县| 朝阳县| 宜黄县| 岑溪市| 聊城市| 湘潭市| 邵武市| 日喀则市| 嘉荫县| 建瓯市| 浦东新区| 满城县| 留坝县| 富裕县| 香格里拉县| 蓬莱市| 绥化市| 剑河县| 萝北县| 邯郸县| 昌邑市| 静安区| 崇文区| 二手房| 铁岭县| 铜川市| 定陶县| 会理县| 团风县| 岳西县| 垣曲县| 龙岩市| 鲁甸县| 霞浦县| 美姑县| 阿尔山市| 万源市| 军事| 南雄市| 吴江市| 敖汉旗|