成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JAVA并發(fā)編程--2.synchronied實現(xiàn)原理

mudiyouyou / 1029人閱讀

摘要:實現(xiàn)原理虛擬機鎖原理虛擬機中對象頭部信息可以看見對象頭中結(jié)構(gòu)中的成員,允許壓縮。否則,將偏向鎖撤銷,升級為輕量級鎖。存在明顯多線程競爭的場景下使用偏向鎖是不合適的,例如生產(chǎn)者消費者隊列。

synchronied實現(xiàn)原理 虛擬機鎖原理

虛擬機中對象頭部信息

/*hotspot/src/share/vm/oops/oop.hpp*/
class oopDesc {
  friend class VMStructs;
 private:
  volatile markOop  _mark;
  union _metadata {
    Klass*      _klass;
    narrowKlass _compressed_klass;
  } _metadata;

可以看見對象頭中結(jié)構(gòu)

Mark Word:instanceOopDesc中的_mark成員,允許壓縮。它用于存儲對象的運行時記錄信息,如哈希值、GC分代年齡(Age)、鎖狀態(tài)標志(偏向鎖、輕量級鎖、重量級鎖)、線程持有的鎖、偏向線程ID、偏向時間戳等

元數(shù)據(jù)指針:instanceOopDesc中的_metadata成員,它是聯(lián)合體,可以表示未壓縮的Klass指針(_klass)和壓縮的Klass指針。對應(yīng)的klass指針指向一個存儲類的元數(shù)據(jù)的Klass對象

32位的對象頭結(jié)構(gòu),64位結(jié)構(gòu)略

//  32 bits:
//  --------
//             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
//             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
//             size:32 ------------------------------------------>| (CMS free block)
//             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)

biased_lock 表示是否偏向鎖

lock類型

00 locked 輕量級鎖

01 unlocked 無鎖

10 monitor 排他鎖

11 marked 標記

加鎖過程

代碼片段

// 默認嘗試偏向鎖
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
 //是否使用偏向鎖
 if (UseBiasedLocking) {
    //未到達safepoint,嘗試重偏向
    if (!SafepointSynchronize::is_at_safepoint()) {
      BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
      if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
        return;
      }
    } else { //在safepoint進行撤銷偏向鎖
      assert(!attempt_rebias, "can not rebias toward VM thread");
      BiasedLocking::revoke_at_safepoint(obj);
    }
    //如果走到這里則說明偏向鎖已撤銷,進行slow_enter(加輕量級鎖)
    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
 }

 slow_enter (obj, lock, THREAD) ;
}

// 輕量級鎖
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
  markOop mark = obj->mark();
  assert(!mark->has_bias_pattern(), "should not see bias pattern here");

  if (mark->is_neutral()) { //如果無鎖狀態(tài)
    // 在lock對象上設(shè)置displaced mark word
    lock->set_displaced_header(mark);
    //使用CAS操作交換lock和object的mark word
    if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
      TEVENT (slow_enter: release stacklock) ;
      return ;
    }
    // 如果CAS失敗,則跳到下面inflate(重量級鎖)
  } else
  //如果給相同對象加鎖,則后續(xù)的鎖的displaced mark設(shè)置為NULL(不會重復(fù)上鎖)
  if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
    assert(lock != mark->locker(), "must not re-lock the same lock");
    assert(lock != (BasicLock*)obj->mark(), "don"t relock with same BasicLock");
    lock->set_displaced_header(NULL);
    return;
  }
//標記lock對象為unused,后續(xù)由CMS回收,并調(diào)用inflate(重量級鎖)
lock->set_displaced_header(markOopDesc::unused_mark());
ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}


偏向鎖

在大多數(shù)情況下,線程之間不存在競爭關(guān)系,即一個鎖會被某個線程多次使用。如果每次都需要申請鎖,開銷會比較大。因此出現(xiàn)了偏向鎖,獲取偏向鎖之后,如果是不存在其他線程競爭鎖,那么就不需要調(diào)用CAS來獲取鎖,以達到減少I/O的目的

加鎖過程

case 1:當該對象第一次被線程獲得鎖的時候,發(fā)現(xiàn)是匿名偏向狀態(tài),則會用CAS指令,將mark word中的thread id由0改成當前線程Id。如果成功,則代表獲得了偏向鎖,繼續(xù)執(zhí)行同步塊中的代碼。否則,將偏向鎖撤銷,升級為輕量級鎖。

case 2:當被偏向的線程再次進入同步塊時,發(fā)現(xiàn)鎖對象偏向的就是當前線程,在通過一些額外的檢查后(細節(jié)見后面的文章),會往當前線程的棧中添加一條Displaced Mark Word為空的Lock Record中,然后繼續(xù)執(zhí)行同步塊的代碼,因為操縱的是線程私有的棧,因此不需要用到CAS指令;由此可見偏向鎖模式下,當被偏向的線程再次嘗試獲得鎖時,僅僅進行幾個簡單的操作就可以了,在這種情況下,synchronized關(guān)鍵字帶來的性能開銷基本可以忽略。

case 3.當其他線程進入同步塊時,發(fā)現(xiàn)已經(jīng)有偏向的線程了,則會進入到撤銷偏向鎖的邏輯里,一般來說,會在safepoint中去查看偏向的線程是否還存活,如果存活且還在同步塊中則將鎖升級為輕量級鎖,原偏向的線程繼續(xù)擁有鎖,當前線程則走入到鎖升級的邏輯里;如果偏向的線程已經(jīng)不存活或者不在同步塊中,則將對象頭的mark word改為無鎖狀態(tài)(unlocked),之后再升級為輕量級鎖。

由此可見,偏向鎖升級的時機為:當鎖已經(jīng)發(fā)生偏向后,只要有另一個線程嘗試獲得偏向鎖,則該偏向鎖就會升級成輕量級鎖。當然這個說法不絕對,因為還有批量重偏向這一機制。

解鎖過程

當有其他線程嘗試獲得鎖時,是根據(jù)遍歷偏向線程的lock record來確定該線程是否還在執(zhí)行同步塊中的代碼。因此偏向鎖的解鎖很簡單,僅僅將棧中的最近一條lock record的obj字段設(shè)置為null。需要注意的是,偏向鎖的解鎖步驟中并不會修改對象頭中的thread id。

批量重偏向與撤銷

從上文偏向鎖的加鎖解鎖過程中可以看出,當只有一個線程反復(fù)進入同步塊時,偏向鎖帶來的性能開銷基本可以忽略,但是當有其他線程嘗試獲得鎖時,就需要等到safe point時將偏向鎖撤銷為無鎖狀態(tài)或升級為輕量級/重量級鎖。safe point這個詞我們在GC中經(jīng)常會提到,其代表了一個狀態(tài),在該狀態(tài)下所有線程都是暫停的(大概這么個意思),詳細可以看這篇文章??傊?,偏向鎖的撤銷是有一定成本的,如果說運行時的場景本身存在多線程競爭的,那偏向鎖的存在不僅不能提高性能,而且會導(dǎo)致性能下降。因此,JVM中增加了一種批量重偏向/撤銷的機制。

存在如下兩種情況:(見官方論文第4小節(jié)):

1.一個線程創(chuàng)建了大量對象并執(zhí)行了初始的同步操作,之后在另一個線程中將這些對象作為鎖進行之后的操作。這種case下,會導(dǎo)致大量的偏向鎖撤銷操作。

2.存在明顯多線程競爭的場景下使用偏向鎖是不合適的,例如生產(chǎn)者/消費者隊列。

批量重偏向(bulk rebias)機制是為了解決第一種場景。批量撤銷(bulk revoke)則是為了解決第二種場景。

其做法是:以class為單位,為每個class維護一個偏向鎖撤銷計數(shù)器,每一次該class的對象發(fā)生偏向撤銷操作時,該計數(shù)器+1,當這個值達到重偏向閾值(默認20)時,JVM就認為該class的偏向鎖有問題,因此會進行批量重偏向。每個class對象會有一個對應(yīng)的epoch字段,每個處于偏向鎖狀態(tài)對象的mark word中也有該字段,其初始值為創(chuàng)建該對象時,class中的epoch的值。每次發(fā)生批量重偏向時,就將該值+1,同時遍歷JVM中所有線程的棧,找到該class所有正處于加鎖狀態(tài)的偏向鎖,將其epoch字段改為新值。下次獲得鎖時,發(fā)現(xiàn)當前對象的epoch值和class的epoch不相等,那就算當前已經(jīng)偏向了其他線程,也不會執(zhí)行撤銷操作,而是直接通過CAS操作將其mark word的Thread Id 改成當前線程Id。

當達到重偏向閾值后,假設(shè)該class計數(shù)器繼續(xù)增長,當其達到批量撤銷的閾值后(默認40),JVM就認為該class的使用場景存在多線程競爭,會標記該class為不可偏向,之后,對于該class的鎖,直接走輕量級鎖的流程

  product(intx, BiasedLockingBulkRebiasThreshold, 20,       
      "Threshold of number of revocations per type to try to "      
      "rebias all objects in the heap of that type")                

product(intx, BiasedLockingBulkRevokeThreshold, 40,                 
      "Threshold of number of revocations per type to permanently " 
      "revoke biases of all objects in the heap of that type")   
      
輕量級鎖

加鎖過程代碼

CASE(_monitorenter): {
    oop lockee = STACK_OBJECT(-1);
    // 創(chuàng)建一個空堆對象lockee
    CHECK_NULL(lockee);
    // 遍歷stack中的lock對象,尋找是否存在指向?qū)ο鬄榇渔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) { //已存在鎖對象,構(gòu)建一個無鎖狀態(tài)的Displaced Mark Word
    //設(shè)置到Lock Record中去
      entry->set_obj(lockee);
      markOop displaced = lockee->mark()->set_unlocked();
      entry->lock()->set_displaced_header(displaced);
      if (Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
        // 如果CAS替換不成功,代表鎖對象不是無鎖狀態(tài),這時候判斷下是不是鎖重入
        if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
          entry->lock()->set_displaced_header(NULL);
        } else {
          // CAS操作失敗則調(diào)用monitorenter
          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
    }
  }

解鎖過程代碼與加鎖基本相同,省略

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/73021.html

相關(guān)文章

  • 從小白程序員一路晉升為大廠高級技術(shù)專家我看過哪些書籍?(建議收藏)

    摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級技術(shù)專家我看過哪些技術(shù)類書籍。 大家好,我是...

    sf_wangchong 評論0 收藏0
  • 并發(fā)

    摘要:表示的是兩個,當其中任意一個計算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)常可見它的使用,在開始分析它的高并發(fā)實現(xiàn)機制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計 分布式,高可用,和機器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個名詞,今天我們首先來說說分布式。 探究...

    supernavy 評論0 收藏0
  • 并發(fā)

    摘要:表示的是兩個,當其中任意一個計算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)常可見它的使用,在開始分析它的高并發(fā)實現(xiàn)機制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計 分布式,高可用,和機器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個名詞,今天我們首先來說說分布式。 探究...

    ddongjian0000 評論0 收藏0
  • 并發(fā)

    摘要:表示的是兩個,當其中任意一個計算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢娝氖褂?,在開始分析它的高并發(fā)實現(xiàn)機制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計 分布式,高可用,和機器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個名詞,今天我們首先來說說分布式。 探究...

    wangdai 評論0 收藏0
  • 阿里 2021 版最全 Java 并發(fā)編程筆記,看完我才懂了“內(nèi)卷”的真正意義

    摘要:純分享直接上干貨操作系統(tǒng)并發(fā)支持進程管理內(nèi)存管理文件系統(tǒng)系統(tǒng)進程間通信網(wǎng)絡(luò)通信阻塞隊列數(shù)組有界隊列鏈表無界隊列優(yōu)先級有限無界隊列延時無界隊列同步隊列隊列內(nèi)存模型線程通信機制內(nèi)存共享消息傳遞內(nèi)存模型順序一致性指令重排序原則內(nèi)存語義線程 純分享 , 直接上干貨! 操作系統(tǒng)并發(fā)支持 進程管理內(nèi)存管...

    不知名網(wǎng)友 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<