博客 / 詳情

返回

synchronized 原理分析

synchronized 原理分析

synchronized 是Java 提供的同步源語,它為 共享資源 提供了原子性 和 可見性保障,本文通過原子性 和 可見性 二個維度分析其實現原理

sync 原子性

通過 monitor 保證 原子性,具體表現為 monitorenter 和 monitorexit 或 ACC_SYNCHRONIZED 來實現加鎖

加鎖流程如下
sync.jpg

鎖升級流程
  • new 對象時,判斷 是否開啓偏向鎖

    • 開啓偏向鎖,構建匿名偏向鎖(101)
    • 關閉偏向鎖,構建無鎖對象(001)
  • 無鎖(001)遇到 線程加鎖時,直接加自旋鎖/輕量鎖(00)
  • 偏向鎖 遇到 一個線程加鎖時,鎖狀態不變,保存線程ID
  • 偏向鎖 遇到 多個線程交替加鎖時,線程跑到安全點,撤消偏向鎖,升級為自旋鎖/輕量鎖(00)
  • 自旋鎖 是 每個線程通過CAS指令去更新對象頭裏面的markword,如果自旋失敗次數、或自旋等待時間過長,鎖膨脹成重量級鎖(10)
  • 重量級鎖 由 ObjectMonitor 實現,需要由用户態切換到內核態
  • 當競爭不激烈時,重量級鎖 自動降級為輕量鎖

monitorenter 源碼分析

CASE(_monitorenter): {
    // 獲取鎖對象
    oop lockee = STACK_OBJECT(-1);
    // 在線程棧上找到一個空閒的BasicObjectLock對象
    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) {
        // 保存鎖對象,表明當前BasicObjectLock持有鎖對象lockee
        entry->set_obj(lockee); 
        int success = false;
        uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
        markOop mark = lockee->mark();   // 獲取鎖對象的頭部標記信息
        // 獲取沒有hash值的標記位值,這裏為0
        intptr_t hash = (intptr_t) markOopDesc::no_hash; 
        // 判斷使用了偏向鎖
        if (mark->has_bias_pattern()) {
            uintptr_t thread_ident;
            uintptr_t anticipated_bias_locking_value;
            thread_ident = (uintptr_t)istate->thread(); // 獲取線程id
            anticipated_bias_locking_value =
              (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
              ~((uintptr_t) markOopDesc::age_mask_in_place);
            /* anticipated_bias_locking_value為0,表明還沒有批量撤銷偏向鎖,且當前線程
              持有了偏向鎖,直接退出 */
            if  (anticipated_bias_locking_value == 0) {
                // already biased towards this thread, nothing to do
                if (PrintBiasedLockingStatistics) {
                    (* BiasedLocking::biased_lock_entry_count_addr())++;
                }
                success = true;
            }
            else if ((anticipated_bias_locking_value & 
                markOopDesc::biased_lock_mask_in_place) != 0) {
                /* anticipated_bias_locking_value不為0,可能是批量撤銷偏向鎖,需要繼續判斷是否有
                 線程持有偏向鎖,如果其他線程持有偏向鎖,判定發生了衝突,就需要撤銷偏向鎖 */
                markOop header = lockee->klass()->prototype_header();
                if (hash != markOopDesc::no_hash) {
                    header = header->copy_set_hash(hash);
                }
                // CAS將對象頭從mark替換為header撤銷偏向鎖
                if (lockee->cas_set_mark(header, mark) == mark) {
                    if (PrintBiasedLockingStatistics)
                        (*BiasedLocking::revoked_lock_entry_count_addr())++;
                }
            }
            else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
                /* 如果anticipated_bias_locking_value不為0,在批量撤銷偏向鎖時需要更改
                  epoch的值,這裏如果epoch改變了,當前線程需要重偏向 */
                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重偏向
                if (lockee->cas_set_mark(new_header, mark) == mark) {
                    if (PrintBiasedLockingStatistics)
                        (* BiasedLocking::rebiased_lock_entry_count_addr())++;
                }
                else {
                    // CAS失敗,發生了競爭,那麼進入monitorenter
                    CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
                }
                success = true;
            }
            else {
                /* 以上條件均不滿足,表明開啓了偏向鎖,此時偏向鎖狀態為匿名偏向,嘗試CAS
                  將其偏向為當前線程*/
                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);
                // CAS重偏向
                if (lockee->cas_set_mark(new_header, header) == header) {
                    if (PrintBiasedLockingStatistics)
                        (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
                }
                else {
                    // CAS失敗,發生了競爭,那麼進入monitorenter
                    CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry),
                         handle_exception);
                }
                success = true;
            }
        }
        // 沒有獲取到鎖,那麼進入傳統的輕量級鎖
        if (!success) {
            markOop displaced = lockee->mark()->set_unlocked();
            entry->lock()->set_displaced_header(displaced);
            bool call_vm = UseHeavyMonitors;  // 判斷是否直接使用重量級鎖
            /* 如果沒有指定直接使用重量級鎖,那麼通過CAS操作嘗試獲取輕量級鎖,即替換
              頭部指針,指向entry */
            if (call_vm || lockee->cas_set_mark((markOop)entry, displaced) != displaced) {
                // 如果失敗,可能是當前線程輕量級鎖重入,那麼判斷是否是鎖重入
                if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) 
                {
                    // 輕量級鎖重入,不需要設置displaced_header信息
                    entry->lock()->set_displaced_header(NULL); 
                } else {
                    // 否則調用monitorenter
                    CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), 
                        handle_exception);
                }
            }
        }
        UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
    } else {
        // 如果未找到,設置more_monitors標誌位,由解釋器分配新的BasicObjectLock並重試
        istate->set_msg(more_monitors);
        UPDATE_PC_AND_RETURN(0);   // Re-execute
    }
}

sync 可見性

sync 通過 緩存一致性協議 保證可見性

MESI

M(modified):修改

E(exclusive):獨佔

S(shared):共享

I(invalid):無效

image.png

sync 和 Lock 的區別

使用

  • sync 自動加鎖、解鎖,Lock 需要手動加鎖、解鎖

功能

  • Lock 支持不同的Condition(不同的等待隊列),指定喚醒
  • Lock 可以使用 tryLock 支持超時

    • sync鎖 不支持超時
  • Lock 可以使用Lock.lockInterruptibly 響應中斷

    • 沒有獲取到 sync鎖 的線程處於 Blocked 狀態不能響應interrupt中斷
  • Lock 支持公平鎖 和 非公平鎖 ,sync 只支持非公平鎖

原理

  • sync 底層由4種不同狀態的鎖升級實現, Lock 由 AQS(state + CLH)實現,屬於樂觀鎖

    • 無鎖、偏向鎖、輕量鎖都屬於用户態
    • 輕量鎖 由CAS實現,屬於樂觀鎖
    • 重量級鎖由 Monitor 實現,屬於悲觀鎖
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.