Java锁源码

Java的锁,绕不开synchronized或者Lock,在“表现层”是这两个关键字,可能对于使用者来说有这不同,或者那不同的问题,但是原理上都是字节码指令,所以如果要学习的话,不要太过分在意语法变更(并不是说不在意),而是应该去理解原理!
我们应该都知道了,java本身是调用Jvm的c++,然后c++去操作一个一个的字节码,而这些字节码的操作,叫做java字节码指令。在上层表示的synchronized跟lock,就是调用了对应的字节码指令,分别叫做_monitorenter、_monitorexit。从名称可以看出“监控进入同步区”、“监控退出同步区”。
先看一个对象,我们只需要记住这个对象的结构:

class BasicLock VALUE_OBJ_CLASS_SPEC {
  friend class VMStructs;
 private:
  volatile markOop _displaced_header;
 public:
  markOop      displaced_header() const               { return _displaced_header; }
  void         set_displaced_header(markOop header)   { _displaced_header = header; }

  void print_on(outputStream* st) const;

  // move a basic lock (used during deoptimization) :在优化过程中使用
  void move_to(oop obj, BasicLock* dest);

  static int displaced_header_offset_in_bytes()       { return offset_of(BasicLock, _displaced_header); }
};
// A BasicObjectLock associates a specific Java object with a BasicLock.
 It is currently embedded in an interpreter frame.
 Because some machines have alignment restrictions on the control stack,
 the actual space allocated by the interpreter may include padding words
 after the end of the BasicObjectLock.  Also, in order to guarantee
 alignment of the embedded BasicLock objects on such machines, we
 put the embedded BasicLock at the beginning of the struct.
 翻译:
基本对象锁将特定的Java对象与基本锁关联起来。

它目前嵌入在解释器框架中。

因为某些机器对控制堆栈有对齐限制,

解释器分配的实际空间可以包括填充字。

基本对象锁定结束后。为了保证

在这样的机器上嵌入的基本锁对象的对齐

将嵌入的基本锁放在结构的开头。
*/
class BasicObjectLock VALUE_OBJ_CLASS_SPEC {
  friend class VMStructs;
 private:
  BasicLock _lock;                                    // the lock, must be double word aligned
  oop       _obj;                                     // object holds the lock;

 public:
  // Manipulation 操纵
  oop      obj() const                                { return _obj;  }
  void set_obj(oop obj)                               { _obj = obj; }
  BasicLock* lock()                                   { return &_lock; }

  // Note: Use frame::interpreter_frame_monitor_size() for the size of BasicObjectLocks
  // in interpreter activation frames since it includes machine-specific padding.
在解释器激活框架中,因为它包括特定于机器的填充。
  static int size()                                   { return sizeof(BasicObjectLock)/wordSize; }

  // GC support
  void oops_do(OopClosure* f) { f->do_oop(&_obj); }

  static int obj_offset_in_bytes()                    { return offset_of(BasicObjectLock, _obj);  }
  static int lock_offset_in_bytes()                   { return offset_of(BasicObjectLock, _lock); }
};

然后直接贴锁指令源码:

/* monitorenter and monitorexit for locking/unlocking an object */
//这两个指令就是对一个对象进行上锁/解锁的操作
      CASE(_monitorenter): {
        oop lockee = STACK_OBJECT(-1);
        // derefing's lockee ought to provoke implicit null check
// derefing的锁定,应引起隐式零校验
        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
   // 因为这是递归输入

/*monitor_base,这个直接返回了_monitor_base,注释的解释是base of monitors on the   native stack,即:本地堆栈的基础监视器*/
        BasicObjectLock* limit = istate->monitor_base();
/*
         stack_base 直接返回了_stack_base,注释解释为:base of expression stack
即:基础栈的表达式
*/
        BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
   /*
此处的操作应该是从基础栈取对象信息,直到该锁锁定的边界位置
如果从基础栈中获取的最近栈的对象为空,则将最近的指针指向entry
如果从基础栈中获取的最近栈的对象为为lock的对象(?),则跳出循环
*/
        BasicObjectLock* entry = NULL;
        while (most_recent != limit ) {
          if (most_recent->obj() == NULL) 
entry = most_recent;
          else if (most_recent->obj() == lockee) 
break;
          most_recent++;
        }
/*
如果入口指针不为null,需要将指针指向的对象,填充被锁对象的值
获取入口指针的未锁定对象头;
将对象头设置到被锁对象的指针上
从而完成被锁内容的赋值操作
保证原子性的cas操作,比较entry跟lockee的对象头地址是否相同,如果相同把entry 地址赋值给displaced(对象头地址);
*/
        if (entry != NULL) {
          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) {
            // Is it simple recursive case?
//THREAD是一个指向一个线程的定义,如果是一个偏向锁,将所的对象头置空
            if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
              entry->lock()->set_displaced_header(NULL);
            } else {
//这里大概是轻量锁/重量锁?具体看下边这个方法的源码
              CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
            }
          }
 //更新pc寄存器,同时继续下一条指令?
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
        } else {
 //entry为空则设置状态为其他监视器,并更新pc寄存器同时返回0
          istate->set_msg(more_monitors);
          UPDATE_PC_AND_RETURN(0); // Re-execute
        }
      }
InterpreterRuntime::monitorenter:
//%note monitor_1
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
  if (PrintBiasedLockingStatistics) {
    Atomic::inc(BiasedLocking::slow_path_entry_count_addr());
  }
  Handle h_obj(thread, elem->obj());
  assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
         "must be NULL or an object");
  //如果使用偏向锁
  if (UseBiasedLocking) {
// Retry fast entry if bias is revoked to avoid unnecessary inflation
//如果取消偏向锁,重试快速进入以避免不必要的膨胀,真的好复杂,        写的有些烦了
    ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
  } else {
    ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
  }
  assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),
         "must be NULL or an object");
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
//如果使用偏向锁
 if (UseBiasedLocking) {
//同步块处于安全点上:表明jvm在此处是安全的,可以暂停
if (!SafepointSynchronize::is_at_safepoint()) {
      //这的意思应该是对偏向锁进行cas交换
      BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
      if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
        return;
      }
    } else {
      assert(!attempt_rebias, "can not rebias toward VM thread");
      BiasedLocking::revoke_at_safepoint(obj);//废弃安全点
    }
    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
 }

 slow_enter (obj, lock, THREAD) ;
}
BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
  assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");

  /* We can revoke the biases of anonymously-biased objects:
   efficiently enough that we should not cause these revocations to
   update the heuristics because doing so may cause unwanted bulk
 revocations (which are expensive) to occur.
翻译:
我们可以撤销匿名偏见对象的偏见:

足够有效,我们不应该造成这些撤销

更新试探法,因为这样做可能会导致不必要的体积。

撤销(很昂贵)发生。
*/
  markOop mark = obj->mark();
  if (mark->is_biased_anonymously() && !attempt_rebias) {//如果是隐士的偏向锁,尝试重置
/* 
We are probably trying to revoke the bias of this object due to
     an identity hash code computation. Try to revoke the bias
     without a safepoint. This is possible if we can successfully
     compare-and-exchange an unbiased header into the mark word of
     the object, meaning that no other thread has raced to acquire
     the bias of the object.
翻译:我们可能试图撤销这个对象的偏向,由于

一个身份哈希代码计算。试图没有安全点情况下消除偏向

。这是可能的,我们能成功比较和交换一个无偏标头到标记字

该对象,即没有其他线程竞争已经获取当前对象的偏向。

PS:实际上可以看出来,此处就是偏向锁的cas过程,找到对象头中关于线程id地址的交换
*/
    markOop biased_value       = mark;//获取对象头
    markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());//设置年龄代
    markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);//对非偏向元素进行地址cas操作
    if (res_mark == biased_value) {
      return BIAS_REVOKED;
    }
  } else if (mark->has_bias_pattern()) {
    Klass* k = Klass::cast(obj->klass());
    markOop prototype_header = k->prototype_header();//获取对象头元素信息
    if (!prototype_header->has_bias_pattern()) {
      /* 
This object has a stale bias from before the bulk revocation
       for this data type occurred. It's pointless to update the
       heuristics at this point so simply update the header with a
       CAS. If we fail this race, the object's bias has been revoked
       by another thread so we simply return and let the caller deal
       with it.
翻译:
此对象在批量撤销之前具有过时的偏向。

这一点上简单地用cas交换,为此发生了数据类型的更新是没有意义的。

如果我们失败了,这个对象的偏见已经被撤销了。

有了它,通过另一个线程,我们简单地返回并让调用方处理。

*/
      markOop biased_value       = mark;///获取偏向值
      markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark);///执行cas操作
      assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked");
      return BIAS_REVOKED;
    } else if (prototype_header->bias_epoch() != mark->bias_epoch()) {
   
      if (attempt_rebias) {
        assert(THREAD->is_Java_thread(), "");
        markOop biased_value       = mark;
        markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
        markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark);
        if (res_mark == biased_value) {
          return BIAS_REVOKED_AND_REBIASED;
        }
      } else {
        markOop biased_value       = mark;
        markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
        markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
        if (res_mark == biased_value) {
          return BIAS_REVOKED;
        }
      }
    }
  }

/** Interpreter/Compiler Slow Case
 This routine is used to handle interpreter/compiler slow case
 We don't need to use fast path here, because it must have been
 failed in the interpreter/compiler code.
编译器/解释器慢案例

本例程是用来解释/编译慢实例句柄

我们不需要使用路径,因为它几乎是在这里,必须有

在解释器的代码/编译失败。
*/
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()) {
    // Anticipate successful CAS -- the ST of the displaced mark must
    // be visible <= the ST performed by the CAS.
    lock->set_displaced_header(mark);
    if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
      TEVENT (slow_enter: release stacklock) ;
      return ;
    }
    // Fall through to inflate() ...
  } else
  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;
  }

#if 0
  // The following optimization isn't particularly useful.
  if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
    lock->set_displaced_header (NULL) ;
    return ;
  }
#endif

  /** The object header will never be displaced to this lock,
   so it does not matter what the value is, except that it
   must be non-zero to avoid looking like a re-entrant lock,
and must not look locked either.
对象头将永远不会移位到这个锁,

所以不管价值是什么,除了它

必须是非零,以避免看起来像一个可重入的锁,

也不能锁上。
  */ 
  lock->set_displaced_header(markOopDesc::unused_mark());
  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}
  CASE(_monitorexit): {
    oop lockee = STACK_OBJECT(-1);
    CHECK_NULL(lockee);
    // derefing's lockee ought to provoke implicit null check
    // find our monitor slot
    BasicObjectLock* limit = istate->monitor_base();
    BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
    while (most_recent != limit ) {
      if ((most_recent)->obj() == lockee) {
        BasicLock* lock = most_recent->lock();
        markOop header = lock->displaced_header();
        most_recent->set_obj(NULL);
        // If it isn't recursive we either must swap old header or call the runtime
        if (header != NULL) {
          if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) {
            // restore object for the slow case
            most_recent->set_obj(lockee);
            CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception);
          }
        }
        UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
      }
      most_recent++;
    }
    // Need to throw illegal monitor state exception
    CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception);
    ShouldNotReachHere();
  }

PS:源码只是解释,接下来,将形成规范化的步骤与流程,解释锁

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356