深入浅出synchronized的原理与源码

本文详细探讨了Java中synchronized的原理,从对象头的锁标识到字节码表示,揭示了monitorenter和monitorexit在确保原子性操作中的作用。同时,通过分析JVM源码,包括bytecodeInterpreter.cpp中的相关函数,展示了synchronized的内部执行流程,如fast_enter、slow_enter、inflate和exit等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.java对象头关于锁的标识

1.对象头

//  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)
//
//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)
//
//  unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
//  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
//  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
//  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
​
//    [JavaThread* | epoch | age | 1 | 01]       lock is biased toward given thread
//    [0           | epoch | age | 1 | 01]       lock is anonymously biased
//
//  - the two lock bits are used to describe three states: locked/unlocked and monitor.
//
//    [ptr             | 00]  locked             ptr points to real header on stack
//    [header      | 0 | 01]  unlocked           regular object header
//    [ptr             | 10]  monitor            inflated lock (header is wapped out)
//    [ptr             | 11]  marked             used by markSweep to mark an object
//                                               not valid at any other time

2.java中synchronized的字节码表示

在java中使用synchronized会在字节码层面生成monitorenter和_monitorexit。这是一个上锁和解锁的过程保证原子性的操作。

3.monitorenter的流程图

4.moniterexit的流程图

2.JVM源码

bytecodeInterpreter.cpp中的_monitorenter

CASE(_monitorenter): {
        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
        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;//如果当前的锁对象时lockee直接返回
          most_recent++;
        }
        if (entry != NULL) {//entry不为空表示有BasicObjectLock,有锁对象可以获取
          entry->set_obj(lockee);//设置当前锁对象为lockee
          int success = false;
          uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;//获取epoch的值
​
          markOop mark = lockee->mark();//获取锁对象头部
          intptr_t hash = (intptr_t) markOopDesc::no_hash;//获取原始的hash值
          // implies UseBiasedLocking
          if (mark->has_bias_pattern()) {//判断是否有偏向锁
            uintptr_t thread_ident;
            uintptr_t anticipated_bias_locking_value;
            thread_ident = (uintptr_t)istate->thread();//获取线程标识
            anticipated_bias_locking_value =
              (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
              ~((uintptr_t) markOopDesc::age_mask_in_place);// 获取原始对象头部 组合 上当前线程地址信息,与当前对象做对比(异或运算,相同为 0,相异为 1,运算后保留为 1 的位也即不同位),随后 与 对象年龄位的相反值做与运算(与运算等价于二进制截断,混沌学堂中说过哈),等价于 :将当前对象头部相对于原始对象头部不同的位,去掉年龄位,得到anticipated_bias_locking_value 
​
            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) {//如果当前持有偏向锁,尝试撤销偏向锁
              // try revoke bias
              markOop header = lockee->klass()->prototype_header();//找到原型头部
              if (hash != markOopDesc::no_hash) {//如果hash值不同
                header = header->copy_set_hash(hash);//头部设置hash值
              }
              if (lockee->cas_set_mark(header, mark) == mark) {//cas将当前锁的头部替换为原型头部,也即尝试撤销偏向锁
                if (PrintBiasedLockingStatistics)
                  (*BiasedLocking::revoked_lock_entry_count_addr())++;
              }
            }
            else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {//如果当前头部已经发生变化,并且epoch不为0,表示已经滚动过,尝试重偏向。这里表示发生过批量锁撤销
              // try rebias
              markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);//获取到当前线程的原型头部
              if (hash != markOopDesc::no_hash) {//hash值不同设置hash值
                new_header = new_header->copy_set_hash(hash);
              }
              if (lockee->cas_set_mark(new_header, mark) == mark) {//cas将当前头部替换为新头部,也即尝试冲片向
                if (PrintBiasedLockingStatistics)
                  (* BiasedLocking::rebiased_lock_entry_count_addr())++;
              }
              else {//cas失败,进入monitorenter.
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
              success = true;
            }
            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)); 因为 匿名偏向锁 中保存着 当前对象头的头部信息(年龄、epoch)所以需要保留下来
              
                if (hash != markOopDesc::no_hash) {///hash值不同设置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);)
              if (lockee->cas_set_mark(new_header, header) == header) {//cas将头部替换为new_header,偏向当前线程
                if (PrintBiasedLockingStatistics)
                  (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
              }
              else {//cas失败进入monitorenter
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
              success = true;
            }
          }
​
          // traditional lightweight locking
          if (!success) {//偏向锁获取失败
            markOop displaced = lockee->mark()->set_unlocked();//将当前头部设置为无锁
            entry->lock()->set_displaced_header(displaced);//替换当前头部
            bool call_vm = UseHeavyMonitors;//默认是false
            if (call_vm || lockee->cas_set_mark((markOop)entry, displaced) != displaced) {//cas将当前头部替换为entry,也即轻量级锁
              // Is it simple recursive case?
              if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {//判断当前线程是否就是上锁的线程
                entry->lock()->set_displaced_header(NULL);//锁重入,将当前lock的头部置为空
              } else {//否则头部替换失败,进入monitorenter
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
            }
          }
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
        } else {
          istate->set_msg(more_monitors);//当前没有锁对象,set_msg,提示需要更多的监视器对象
          UPDATE_PC_AND_RETURN(0); // Re-execute
        }
      }

InterpreterRuntime::monitorenter

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
  Handle h_obj(thread, el
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值