首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Synchronized 源码分析

Synchronized 源码分析

作者头像
itliusir
发布于 2020-02-10 02:51:03
发布于 2020-02-10 02:51:03
1.2K0
举报
文章被收录于专栏:刘君君刘君君

摘要:

  1. Synchronized 怎么做到同步的
  2. Synchronized 做了哪些优化
  3. Synchronized 有哪些缺点

TOP 带着问题看源码

  1. Synchronized 怎么做到同步的
  2. Synchronized 做了哪些优化
  3. Synchronized 有哪些缺点

1. Synchronized 基本介绍

前面我们已经介绍和分析了管程,而 Synchronized 则是 JVM 层面中管程的一种实现,它通过对细节的屏蔽方便了开发人员的使用。

2. 对象的内存结构

在 HotSpot 虚拟机中,对象在堆内存中的存储布局可以划分为三个部分:对象头、实例数据、对齐填充

我们可以通过使用工具 jol 打印对象的结构

obj example

代码语言:javascript
AI代码解释
复制
public class ObjDemo {
    int a;
    String s;
    long l;
}

print obj struct

代码语言:javascript
AI代码解释
复制
import org.openjdk.jol.info.ClassLayout;

public void printObjStruct () {
    System.out.println(
            ClassLayout.parseInstance(new ObjDemo()).toPrintable());
}

result

代码语言:javascript
AI代码解释
复制
OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0     4                    (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                    (object header)                           44 c1 00 f8 (01000100 11000001 00000000 11111000) (-134168252)
     12     4                int ObjDemo.a                                 0
     16     8               long ObjDemo.l                                 0
     24     4   java.lang.String ObjDemo.s                                 null
     28     4                    (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

2.1 对象头

HotSpot 虚拟机对象的对象头包括两类信息,一类是存储对象运行时数据,我们称为 Mark Word ;一类是类型指针,即对象指向它的类型元数据的指针(虚拟机通过该指针确定对象是哪个类的实例),我们称为 klass pointer

2.1.1 Klass Pointer

存放的是该对象对应的类的指针(指向方法区的内存区域)

2.1.2 Mark Word

markOop.hpp

代码语言:javascript
AI代码解释
复制
// 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)

由 markOop 的源码注释,我们可以知道 mark word 在不同的状态下都存哪些东西。

对象的状态一共有五种:无锁(0 01)、可偏向锁(1 01)、轻量级锁(_ 00)、重量级锁(_ 10)、GC状态(_ 11)

注意:

  1. age 一直为4位的原因是我们 gc 的年龄最大是15就会被回收到老年代,所以 4 个 bit位就可以表示
  2. lock 2位只能表示4种状态,加上偏向标志biased lock 1位则可以表示5种状态
  3. epoch 是用来标示是否已经重偏向

通过对象头的结构我们可以知道,同步锁实际是存储在对象头中的,不难推断其同步的方式就是通过对象与线程的绑定占有来实现排他的效果。 Synchronized 关键字在编译后会在同步块的前后生成 monitorentermonitorexit 这两个字节码指令,JVM 会对其进行解析,如果锁的是对象则就是对该对象加锁解锁,如果是类方法则是对 Class 对象加锁解锁,如果是实例方法则是对对应的对象加锁解锁。

回到 TOP 1 问题,可以知道同步是通过对象与线程的绑定记录来实现的。

2.2 实例数据

真正存储的有效信息,默认顺序会按照虚拟机的默认分配顺序, 如果 -XX:CompactFields 参数为 true (默认为true),子类中较小的变量页允许插入到父类变量的空隙中。

2.3 对齐填充

由于 HotSpot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,因此如果对象实例数据部分没有对齐的话就需要对齐填充来补全

3. monitorenter 源码解析

我们主要解析 monitorenter 加锁的例子。

3.1 入口

HotSpot 虚拟机实现了两种的解释器,分别是模板解释器 和 C++解释器,默认使用的是模板解释器。

C++ 解释器也即是我们平时用来实现功能的方法,简单明了但是很慢;模板解释器是跳过了编译器,自己使用汇编代码来做的,比较难懂。

所以 monitorenter 的两个入口,我们从 C++ 解释器的入口分析,更加容易明白。

3.2 偏向锁&轻量级锁

3.2.1 偏向锁的获取
代码语言:javascript
AI代码解释
复制
CASE(_monitorenter): {
  oop lockee = STACK_OBJECT(-1);
  CHECK_NULL(lockee);
  // 寻找空闲的锁记录(Lock Record) 空间
  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++;
  }
  // 存在空闲的Lock Record
  if (entry != NULL) {
    // Lock Record 的 obj 指针指向锁对象
    entry->set_obj(lockee);
    int success = false;
    uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
    markOop mark = lockee->mark();
    intptr_t hash = (intptr_t) markOopDesc::no_hash;
    // 如果锁对象的对象头标志是偏向模式(1 01)
    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
      anticipated_bias_locking_value =
        // 将线程id与prototype_header(epoch、分代年龄、偏向模式、锁标志)部分相或
        (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) 
        // 与锁对象的markword异或,相等为0
         ^ (uintptr_t)mark) 
        // 将上面结果中的分代年龄忽略掉
        &~((uintptr_t) markOopDesc::age_mask_in_place);
			// ① 为0代表偏向线程是当前线程 且 对象头的epoch与class的epoch相等,什么也不做
      if  (anticipated_bias_locking_value == 0) {
        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) {
          header = header->copy_set_hash(hash);
        }
        if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
          if (PrintBiasedLockingStatistics)
            (*BiasedLocking::revoked_lock_entry_count_addr())++;
        }
      }
      // ③ 锁对象头的 epoch 与 class 的 epoch 不相等,尝试重偏向
      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);
        }
        if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
          if (PrintBiasedLockingStatistics)
            (* BiasedLocking::rebiased_lock_entry_count_addr())++;
        }
        else {
          // 有竞争重偏向失败,调用 monitorenter 锁升级
          CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
        }
        success = true;
      }
      // ④ 未偏向任何线程,尝试偏向
      else {
        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);)
        // CAS 尝试修改
        if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {
          if (PrintBiasedLockingStatistics)
            (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
        }
        // 有竞争偏向失败,调用 monitorenter 锁升级
        else {
          CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
        }
        success = true;
      }
    }

    // 走到这里说明偏向的不是当前线程或没有开启偏向锁等原因
    if (!success) {
      // 轻量级锁逻辑 start
      // 构造无锁状态 Mark Word 的 copy(Displaced Mark Word)
      markOop displaced = lockee->mark()->set_unlocked();
      // 将锁记录空间(Lock Record)指向Displaced Mark Word
      entry->lock()->set_displaced_header(displaced);
      // 是否禁用偏向锁和轻量级锁
      bool call_vm = UseHeavyMonitors;
      if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
        // 判断是不是锁重入,是的话把Displaced Mark Word设置为null来表示重入
        // 置null的原因是因为要记录重入次数,但是mark word大小有限,所以每次重入都在栈帧中新增一个Displaced Mark Word为null的记录
        if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
          entry->lock()->set_displaced_header(NULL);
        } else {
          // 若禁用则锁升级
          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
  }
}

回到 TOP 2 问题,可以知道在真正系统同步之前对竞争态小的做了偏向锁和轻量级锁的优化。

3.2.2 偏向锁的撤销

这里的撤销并不是锁的释放,而是尝试获取偏向锁因为不满足条件把锁改为非偏向锁状态

  • JavaThread* thread 是指 java 中当前线程
  • BasicObjectLock* elem 包含对象头数据和 oop 指针
  • UseBiasedLocking 是指是否启动偏向锁标识,JVM 启动默认是启动偏向锁

获取偏向锁失败会进入下面逻辑,如果是支持偏向锁,走 fast_enter ,否则走 slow_enter

interpreterRuntime.cpp

代码语言:javascript
AI代码解释
复制
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
	...
  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);
  }
  ...
IRT_END

synchronizer.cpp

代码语言:javascript
AI代码解释
复制
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
 // 再次判断是否开启偏向锁
 if (UseBiasedLocking) {
   	// 未处于全局安全点
    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 {
      // 处于全局安全点,撤销偏向锁
      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.cpp

代码语言:javascript
AI代码解释
复制
BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
  // 获取对象头的 Mark Word
  markOop mark = obj->mark();
  // ① 是否是可偏向状态(后三位是否为 1 01) 且 Thread ID 为 null 且 attempt_rebias 为 false(如锁对象的hashcode方法被调用),需要撤销偏向锁
  if (mark->is_biased_anonymously() && !attempt_rebias) {
    markOop biased_value       = mark;
    // 构造非偏向的markword
    markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
    // CAS 设置重新设置偏向锁状态(撤销)
    markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
    if (res_mark == biased_value) {
      return BIAS_REVOKED;
    }
  // ② 是否是可偏向状态(后三位是否为 1 01)
  } else if (mark->has_bias_pattern()) {
    Klass* k = obj->klass();
    markOop prototype_header = k->prototype_header();
    // ②-① 已经有线程对对象做了锁定,需要撤销偏向锁
    if (!prototype_header->has_bias_pattern()) {
      markOop biased_value       = mark;
      markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark);
      return BIAS_REVOKED;
    // ②-② epoch 不相等,说明重偏向过,已过期,需要撤销偏向锁
    } 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;
        }
      }
    }
  }
  // 批量重偏向 批量撤销
  HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias);
  if (heuristics == HR_NOT_BIASED) {
    return NOT_BIASED;
  } else if (heuristics == HR_SINGLE_REVOKE) {
    Klass *k = obj->klass();
    markOop prototype_header = k->prototype_header();
    // 需要撤销的是偏向当前线程的锁
    if (mark->biased_locker() == THREAD &&
        prototype_header->bias_epoch() == mark->bias_epoch()) {
      ResourceMark rm;
      if (TraceBiasedLocking) {
        tty->print_cr("Revoking bias by walking my own stack:");
      }
      BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD);
      ((JavaThread*) THREAD)->set_cached_monitor_info(NULL);
      assert(cond == BIAS_REVOKED, "why not?");
      return cond;
    } else {
      // 调用revoke_bias方法
      // revoke_bias 主要逻辑
      // 1. 查看当前线程是否存活,不存活直接撤销
      // 2. 偏向的线程如果不在同步块直接撤销(通过遍历线程栈的Lock Record来判断)
      // 3. 轻量级锁逻辑
      VM_RevokeBias revoke(&obj, (JavaThread*) THREAD);
      VMThread::execute(&revoke);
      return revoke.status_code();
    }
  }

  assert((heuristics == HR_BULK_REVOKE) ||
         (heuristics == HR_BULK_REBIAS), "?");
  // 批量撤销、重定向
  VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD,
                                (heuristics == HR_BULK_REBIAS),
                                attempt_rebias);
  VMThread::execute(&bulk_revoke);
  return bulk_revoke.status_code();
}

3.3 重量级锁

synchronizer.cpp

代码语言:javascript
AI代码解释
复制
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 ;
    }
  // 轻量级锁重入
  } 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;
  }
	...
  // 锁标记为_ 10,膨胀为重量级锁,调用 enter 方法
  lock->set_displaced_header(markOopDesc::unused_mark());
  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}
3.3.1 膨胀过程

膨胀过程很简单,就是对不同状态的对象进行不同的处理。

代码语言:javascript
AI代码解释
复制
ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
  assert (Universe::verify_in_progress() ||
          !SafepointSynchronize::is_at_safepoint(), "invariant") ;

  for (;;) {
      const markOop mark = object->mark() ;
    	// ① 如果已经是重量级锁状态直接返回
      if (mark->has_monitor()) {
          ObjectMonitor * inf = mark->monitor() ;
          return inf ;
      }
    	// ② 如果是正在膨胀中,重试等待另一个膨胀完
			if (mark == markOopDesc::INFLATING()) {
         TEVENT (Inflate: spin while INFLATING) ;
         ReadStableMark(object) ;
         continue ;
      }
    	// ③ 如果是轻量级锁状态
			if (mark->has_locker()) {
        	// 先初始化 ObjectMonitor 对象
          ObjectMonitor * m = omAlloc (Self) ;
          m->Recycle();
          m->_Responsible  = NULL ;
          m->OwnerIsThread = 0 ;
          m->_recursions   = 0 ;
          m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;   // Consider: maintain by type/class
					// 设置状态为膨胀中
          markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
          if (cmp != mark) {
             omRelease (Self, m, true) ;
             continue ;       // Interference -- just retry
          }
          markOop dmw = mark->displaced_mark_helper() ;
          // Setup monitor fields to proper values -- prepare the monitor
          m->set_header(dmw) ;
          m->set_owner(mark->locker());
          m->set_object(object);
          guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;
        	// 将锁对象头设置为重量级锁状态
          object->release_set_mark(markOopDesc::encode(m));

          if (ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;
          TEVENT(Inflate: overwrite stacklock) ;
          if (TraceMonitorInflation) {
            if (object->is_instance()) {
              ResourceMark rm;
              tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
                (void *) object, (intptr_t) object->mark(),
                object->klass()->external_name());
            }
          }
          return m ;
      }
			// ④ 如果是无锁状态
      ObjectMonitor * m = omAlloc (Self) ;
      // prepare m for installation - set monitor to initial state
      m->Recycle();
      m->set_header(mark);
      m->set_owner(NULL);
      m->set_object(object);
      m->OwnerIsThread = 1 ;
      m->_recursions   = 0 ;
      m->_Responsible  = NULL ;
      m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;       // consider: keep metastats by type/class
			// CAS 设置对象头标志为重量级锁
      if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
          // 有竞争设置失败,释放monitor重试
        	m->set_object (NULL) ;
          m->set_owner  (NULL) ;
          m->OwnerIsThread = 0 ;
          m->Recycle() ;
          omRelease (Self, m, true) ;
          m = NULL ;
          continue ;
      }

      // Hopefully the performance counters are allocated on distinct
      // cache lines to avoid false sharing on MP systems ...
      if (ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;
      TEVENT(Inflate: overwrite neutral) ;
      if (TraceMonitorInflation) {
        if (object->is_instance()) {
          ResourceMark rm;
          tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
            (void *) object, (intptr_t) object->mark(),
            object->klass()->external_name());
        }
      }
      return m ;
  }
}
3.3.2 获取重量级锁
代码语言:javascript
AI代码解释
复制
void ATTR ObjectMonitor::enter(TRAPS) {
  Thread * const Self = THREAD ;
  void * cur ;
	// 无锁状态,通过 CAS 直接获得锁
  cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
  if (cur == NULL) {
     return ;
  }
	// 锁重入,计数,直接返回
  if (cur == Self) {
     _recursions ++ ;
     return ;
  }
	// 当前线程之前是轻量级锁状态,重置owner为当前线程(之前是执行锁记录(Lock Record)的指针),重置重入计数为1
  if (Self->is_lock_owned ((address)cur)) {
    assert (_recursions == 0, "internal state error");
    _recursions = 1 ;
    _owner = Self ;
    OwnerIsThread = 1 ;
    return ;
  }
  Self->_Stalled = intptr_t(this) ;
  // 调用系统同步操作之前,先尝试自旋获得锁
  if (Knob_SpinEarly && TrySpin (Self) > 0) {
     Self->_Stalled = 0 ;
     return ;
  }
	
  JavaThread * jt = (JavaThread *) Self ;
  Atomic::inc_ptr(&_count);
  EventJavaMonitorEnter event;

  { 
    ...
    for (;;) {
      jt->set_suspend_equivalent();
      EnterI (THREAD) ;
      if (!ExitSuspendEquivalent(jt)) break ;
      _recursions = 0 ;
      _succ = NULL ;
      exit (false, Self) ;

      jt->java_suspend_self();
    }
    Self->set_current_pending_monitor(NULL);
  }
  ...
}
3.3.3 调用 EnterI 方法获得锁

核心逻辑分为三步:

  1. 将当前线程封装为 node 塞到队列 cxq 的队头
  2. 调用 park 挂起当前线程
  3. 被唤醒后再次尝试获取锁(在唤醒时候会根据不同的唤醒策略定义 cxq 与 EntryList 的优先级)
代码语言:javascript
AI代码解释
复制
void ATTR ObjectMonitor::EnterI (TRAPS) {
    Thread * Self = THREAD ;
    // 尝试获取锁
    if (TryLock (Self) > 0) {
        return ;
    }

    DeferredInitialize () ;
		// 自旋
    if (TrySpin (Self) > 0) {
        return ;
    }
		// 如果没获取到锁(锁被占用)就把线程封装到node节点中
    ObjectWaiter node(Self) ;
    Self->_ParkEvent->reset() ;
    node._prev   = (ObjectWaiter *) 0xBAD ;
    node.TState  = ObjectWaiter::TS_CXQ ;
    ObjectWaiter * nxt ;
    for (;;) {
      	// 将node节点插入到 cxq 队列的头部
        node._next = nxt = _cxq ;
      	// CAS 成功直接返回
        if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
        // CAS 失败走这里,重试获取锁
        if (TryLock (Self) > 0) {
            return ;
        }
    }
		// 如果没有等待的线程,把_Responsible设置为自己
    if ((SyncFlags & 16) == 0 && nxt == NULL && _EntryList == NULL) {
        Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;
    }
    TEVENT (Inflated enter - Contention) ;
    int nWakeups = 0 ;
    int RecheckInterval = 1 ;

    for (;;) {
				// 再次尝试获取锁
        if (TryLock (Self) > 0) break ;

        if ((SyncFlags & 2) && _Responsible == NULL) {
           Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;
        }
			
        // 调用 park 函数挂起当前线程
        if (_Responsible == Self || (SyncFlags & 1)) {
            TEVENT (Inflated enter - park TIMED) ;
            Self->_ParkEvent->park ((jlong) RecheckInterval) ;
            // Increase the RecheckInterval, but clamp the value.
            RecheckInterval *= 8 ;
            if (RecheckInterval > 1000) RecheckInterval = 1000 ;
        } else {
            TEVENT (Inflated enter - park UNTIMED) ;
            Self->_ParkEvent->park() ;
        }

        if (TryLock(Self) > 0) break ;
        TEVENT (Inflated enter - Futile wakeup) ;
        if (ObjectMonitor::_sync_FutileWakeups != NULL) {
           ObjectMonitor::_sync_FutileWakeups->inc() ;
        }
        ++ nWakeups ;
        if ((Knob_SpinAfterFutile & 1) && TrySpin (Self) > 0) break ;
        if ((Knob_ResetEvent & 1) && Self->_ParkEvent->fired()) {
           Self->_ParkEvent->reset() ;
           OrderAccess::fence() ;
        }
      	// 在释放锁时,_succ会被设置为 EntryList 或 cxq 中的一个线程
        if (_succ == Self) _succ = NULL ;

        // Invariant: after clearing _succ a thread *must* retry _owner before parking.
        OrderAccess::fence() ;
    }
  	// 上个死循环如果结束意味着获取到锁了
  	// 将当前线程 node 从 EntryList 或 cxq 移除
		UnlinkAfterAcquire (Self, &node) ;
    if (_succ == Self) _succ = NULL ;
    if (_Responsible == Self) {
        _Responsible = NULL ;
        OrderAccess::fence(); // Dekker pivot-point
    }
    if (SyncFlags & 8) {
       OrderAccess::fence() ;
    }
    return ;
}

4. 总结

可以看到 Synchronized 的加锁过程非常复杂,但是核心设计还是在不同的竞争态选择不同的优化模式,尽量使同步的开销减少到最小。而重量级的实现可以发现就是管程的一种实现模式,对比并发包的 Java 层面实现的管程,Synchronized 的条件变量等待队列比较单一,只有 wait 一种。虽然灵活性不如并发包的锁,但是在异常不可控和代码维护方面 Synchronized 无疑是更好。

回到 TOP 3 问题,可以知道 Synchronized 缺点就是不灵活(条件变量、代码使用、超时时间),不公平,不能响应中断。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-11-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
2019可信云&云计算开源产业大会:腾讯云TStack获多云管理认证及多项殊荣
7月2日-3日,工信部指导,中国信息通信研究院主办的2019年可信云大会与2019年云计算开源产业大会顺利召开。继通过可信云·开源解决方案、混合云解决方案等认证后,腾讯云TStack凭借强大的多云管理能力,获得可信云·多云管理认证。 同时,基于产品技术能力及业内突出影响,腾讯云TStack再一举拿下“可信云技术创新奖”、“尖峰开源技术创新奖”两项殊荣。截至此次大会,腾讯云已累计通过30余项可信云认证,成为可信云体系认证中最全云厂商之一。 // 可信云认证,腾讯云TStack多云管理能力获权威认可 //
腾讯云TStack
2019/07/05
1.2K0
2019可信云&云计算开源产业大会:腾讯云TStack获多云管理认证及多项殊荣
腾讯云TStack 开源解决方案通过可信云认证
8月14日,在数据中心联盟组织和中国信息通信研究院联合举办的可信云大会中,腾讯云TStack通过现场测试和专家评估获得了《可信云-开源解决方案》认证证书。 此次认证的测试评估主要围绕云平台基础功能、运维系统功能、文档完备性、可靠性、互操作性、安全性、资源调配能力、异构虚拟化和资源调度效率九项测试指标的33项测试用例展开,结果显示,腾讯云TStack解决方案在技术能力和服务完备性规范性方面均达到可信云开源解决方案认证评估标准。 此外,腾讯云TStack已通过可信云-混合云解决方案认证,监控云平台已通过国家级云
腾讯云TStack
2018/08/14
2.3K0
这个领域,腾讯云做到了国内第一
七月才过去两天,好消息却不断。 这不,我们刚中标长沙市城市超级大脑项目,就又拿了4项大奖,11项认证,成为国内最全的可信云认证厂商。看看具体是怎么回事。 7月2日,在中国信息通信研究院、中国通信标准化协会主办的“2019可信云大会”上,腾讯云一举获得了可信智能云、可信政务云(数字广东)、内容安全、政务云综合水平、多云管理平台、蓝鲸容器解决方案、微服务、智能云使能平台(机器学习)、以及云服务用户数据保护能力分级评估等领域11项可信云认证,并在技术和行业应用领域获得4项技术创新和行业应用大奖。 在备受
腾讯云TStack
2019/07/05
1.6K0
这个领域,腾讯云做到了国内第一
权威!353 家通过 1198 项可信云评估:涉及虚拟化、超融合、容器、安全、数据库、AI、混合云、网络、金融、运维、边缘等
中国信通院可信云发展十年,为云计算产业建立一套完整的标准以及评估体系,涉及云服务、云软件、云原生、高性能计算、软件开发、混合云、云网、云管、云边协同、行业云、云安全、开源、数字化等多个方面。 可信云评估体系的系列标准及评估结果已经成为政府支撑、行业规范、用户选型的重要参考。 截至目前,累计开展16个批次评估,通过项目1198项。 虚拟化云平台分级先进级(首批) 虚拟化云平台性能评估(大规模) 超融合解决方案(首批) 云桌面解决方案(首批) 混沌工程平台能力(首批) 高性能计算云平台(首批) 容
云头条
2022/03/18
1.8K0
升级为私有全栈云的腾讯云TStack究竟强在了哪儿?
在刚刚结束的美国丹佛Open Infrastructure峰会上,腾讯云对全新升级为私有全栈云的TStack进行了详细的介绍,引起峰会现场众多OpenStack专业人士注目。那么,这款腾讯基于OpenStack倾力打造的私有云解决方案,究竟有何过人之处?升级为私有全栈云的TStack究竟强在了什么地方?带着这些疑问,老孙对腾讯云TStack产品总监邬沛君、腾讯云TStack首席架构师贺阮博士就这些问题跟大家聊聊。
腾讯技术工程官方号
2019/05/20
2K0
升级为私有全栈云的腾讯云TStack究竟强在了哪儿?
专访腾讯产品总监邬沛君:TStack斩获OSCAR技术创新奖的背后
导读:作为中国云计算开源领域最专业、最高端、最具规模的行业盛会,2018云计算开源产业大会(全球云计算开源大会)由工业和信息化部指导,中国信息通信研究院主办、云计算开源产业联盟承办。大会于2018年3月21日在国家会议中心盛大启幕。腾讯云TStack凭借强大的技术创新能力,斩获OSCAR技术创新奖。为了了解TStack的价值和核心优势,我们对腾讯技术工程事业群的腾讯云TStack产品总监邬沛君进行了专访。 请为我们简单的介绍下TStack。 邬:TStack是诞生于腾讯内部的私有云使用场景,是总结公司自
腾讯技术工程官方号
2018/04/23
1.5K0
专访腾讯产品总监邬沛君:TStack斩获OSCAR技术创新奖的背后
腾讯云拿奖到手软:六项最佳实践,十多项可信云认证
点击上方“腾讯云TStack”,关注我们,获取最in云端资讯和海量技术干货~ 7月29-30日,在中国信息通信研究院、中国通信标准化协会主办的“2020可信云线上大会”上,腾讯云拿下了四项最佳实践奖项: 可信云年度服务最佳实践: 腾讯云金融专区、T-Sec云防火墙 可信云年度技术最佳实践: 腾讯云虚拟化技术、大数据容器化技术 还通过了十多项可信云评估认证,领域、数量均领先同行: Serverless云函数获国内FaaS领域首张可信云认证,代码编号“FaaS-001” 腾讯云TStack获可信云云管理
腾讯云TStack
2020/07/31
1K0
喜讯!腾讯云独家当选2017中国混合云最佳用户
2017年11月28日,由中国信息通信研究院指导,云计算开源产业联盟(OSCAR)主办、IT168承办的“中国混合云优秀案例暨寻找2017中国混合云十大用户”评选结果正式揭晓。腾讯云摘得了本活动唯一奖项“2017年度中国混合云最佳用户”,同时腾讯IT 混合云获选2017年度中国混合云优秀案例。 云计算开源产业联盟是由工业和信息化部信息化和软件服务业司指导,中国信息通信研究院联合多家云计算开源技术公司成立,中国通信标准化协会代管的第三方非营利机构。 据悉,举办此次评选活动历时3个月,面向全国征集混合云案例
腾讯云TStack
2018/03/21
2K0
喜讯!腾讯云独家当选2017中国混合云最佳用户
2020年可信云大会云端开启,发布《云计算发展白皮书》
7月29-30日,由中国信息通信研究院(以下简称“中国信通院”)、中国通信标准化协会联合主办的“2020可信云大会”在线上盛大开幕。工业和信息化部信息通信发展司司长闻库、中国信通院院长刘多、农业农村部信息中心副主任王文生出席会议并致辞。会上发布了《云计算发展白皮书(2020)》《云原生发展白皮书》等一系列重量级成果,披露了可信云最新评估结果,数字农业产业推进方阵在会上正式成立。大会由央视频、央视新闻移动网、新华社客户端、IT大咖说、微吼等平台全程直播,在线观看人数超过864.1万次。
云加小助手
2020/07/29
1.8K0
官宣!腾讯云TStack与IBM LinuxONE互认证
2019年1月21日,腾讯云TStack与IBM LinuxONE通过兼容性认证,这是腾讯与IBM在云计算领域的一次通力合作。
腾讯云TStack
2019/03/01
12.7K0
官宣!腾讯云TStack与IBM LinuxONE互认证
腾讯开源斩获5项大奖,1项独家认证
7月3日,由中国信息通信研究院主办的2019云计算开源产业大会在北京举办。腾讯开源独揽尖峰开源企业奖、尖峰开源技术创新奖(自主研发项目)、尖峰开源技术创新奖(基于社区版本二次开发项目)、行业开源技术领航奖和尖峰开源人物等5项大奖,并成为首个通过可信开源治理能力评估认证的自发开源企业。 就在7月2日,腾讯在GitHub上的Star数累计达到23万,在全球企业开源Star数排名中跃居前十。 腾讯开源管理办公室执行总监许勇表示,“我们最近刚发布了腾讯开源路线图,通过‘三步走’的开源计划,不断深化腾讯已有的技术能
腾讯云TStack
2019/07/05
6.8K0
腾讯开源斩获5项大奖,1项独家认证
腾讯Q1财报:腾讯云持续高速增长,AI、出海成关键词
云资讯小编
2017/05/17
3.4K0
腾讯Q1财报:腾讯云持续高速增长,AI、出海成关键词
一举通过多项可信云认证?可以,这很互联港湾!
近日,云计算行业的奥斯卡——可信云服务认证最新评选结果正式公布,这项成功通过了国际标准ITU-T的云服务认证通过全面的云服务评估体系,为用户选择可信、安全的云服务提供可信赖的指导意见。凭借多年的技术积
港湾人儿
2018/07/02
6820
腾讯云TStack获2018年全球云计算开源产业大会OSCAR技术创新奖
作为中国云计算开源领域最专业、最高端、最具规模的行业盛会,2018云计算开源产业大会(全球云计算开源大会)由工业和信息化部指导,中国信息通信研究院主办、云计算开源产业联盟承办。大会于2018年3月21日在国家会议中心盛大启幕。腾讯云TStack凭借强大的技术创新能力,斩获OSCAR技术创新奖。 ▲图为腾讯云TStack架构师潘晓东上台领奖(左二) 大会为期两天,包含主题演讲、成果发布、专题论坛、展览展示、现场互动等多个环节,核心内容涵盖基础设施、容器、机器学习、存储、数据库、运维等诸多
腾讯云TStack
2018/04/02
1.2K0
腾讯云TStack获2018年全球云计算开源产业大会OSCAR技术创新奖
新发布就获可信云认证,腾讯云政务专区来了!
点击上方“腾讯云TStack”,关注我们,获取最in云端资讯和海量技术干货~ 7月29-30日,由中国信息通信研究院(以下简称“中国信通院”)、中国通信标准化协会联合主办的“2020可信云大会”线上峰会盛大开幕。会上,腾讯云揽获多项奖项,并新增十多项可信云评估认证,在可信云认证产品数量和范围中持续保持领先。 其中腾讯云政务专区通过《面向公有云模式的政务云服务》测评,腾讯云也是首批通过该测评的云服务厂商,同时还通过了政务云解决方案评估。腾讯云政务专区以新基建数字底座为基础,专为政务行业量身定制的云产品,既
腾讯云TStack
2020/08/03
2.3K0
腾讯云TStack白皮书
腾讯云TStack是腾讯基于自身强大技术能力和海量运营经验推出的私有云平台,提供集IaaS、PaaS和SaaS为一体的综合云服务解决方案;支持对国产CPU的虚拟化,并通过云管理平台软件提供云主机、云存储、云网络和云负载均衡等IaaS层基础功能。大量私有化部署经验,具有高稳定性、统一管理、可视化运营等特点,强兼容性可与国产服务器实现良好适配,助力政府、企业构建稳定安全的云环境和健康的云生态。
腾讯云TStack
2020/06/05
9.1K7
七项安全技术代表厂商!腾讯云再获Gartner®认可
近日,国际研究机构Gartner® 发布《2023中国网络安全技术成熟度曲线(Hype Cycle for Security in China, 2023)》报告,腾讯云在零信任、云工作负载保护平台、态势感知、攻击面管理、机密计算、多方安全计算和开发安全等七大技术领域被列为代表厂商,再度获得Gartner®认可。
小腾资讯君
2023/10/26
5900
荣获两项认证!腾讯云WeCity未来城市再获新成就!
7月28日,在中国信息通信研究院、中国通信标准化协会联合主办的“2021可信云大会”上,腾讯云拿下了5项大奖和12项可信云认证。 ▼▼▼ 可信云认证评估是国内目前唯一针对云服务的权威认证体系,是业内公认最具权威和市场认可度的云计算标准。 目前,腾讯云已经在云主机、云存储、云数据库等各细分领域评测中,获得54项可信云认证,数量位居中国云厂商第一。 其中,腾讯云WeCity技术平台荣获“数字政府智慧中台评估”认证,使用腾讯专有云(TCE)的“数智贵阳”云平台获得“专有云模式的政务云服务评估”认证。  WeCi
云产品技术支持小助手
2022/06/24
9260
荣获两项认证!腾讯云WeCity未来城市再获新成就!
叮咚!您有一份安全易用的私有云解决方案待查收
TStack是腾讯云基于自身强大技术能力和海量运营经验推出的私有云平台,提供集IaaS、PaaS和SaaS为一体的综合云服务解决方案;大量私有化部署经验,具备混合云管理能力,具有高稳定性、统一管理、可视化运营等特点,助力政府、企业构建稳定安全的云环境和健康的云生态。 客户价值 Customer Value 架构开放兼容 基于开源的技术架构,具备多平台兼容能力和标准的API服务,支持第三方定制开发 产品形态多样 接入大量腾讯内部成熟的PaaS和SaaS服务,便于客户根据需求完善应用服务体系 海量
腾讯云TStack
2018/09/05
1.7K0
叮咚!您有一份安全易用的私有云解决方案待查收
云计算开源大势所趋,2019云计算开源产业大会将于7月开幕
云计算已经由概念导入期进入了应用繁荣、迅速普及的新阶段。在云计算的发展过程中,开源软件已经成为云计算技术进步、产业发展中的重要推动力量。
CloudBest
2019/08/02
1K0
云计算开源大势所趋,2019云计算开源产业大会将于7月开幕
推荐阅读
相关推荐
2019可信云&云计算开源产业大会:腾讯云TStack获多云管理认证及多项殊荣
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • TOP 带着问题看源码
  • 1. Synchronized 基本介绍
  • 2. 对象的内存结构
    • 2.1 对象头
      • 2.1.1 Klass Pointer
      • 2.1.2 Mark Word
    • 2.2 实例数据
    • 2.3 对齐填充
  • 3. monitorenter 源码解析
    • 3.1 入口
    • 3.2 偏向锁&轻量级锁
      • 3.2.1 偏向锁的获取
      • 3.2.2 偏向锁的撤销
    • 3.3 重量级锁
      • 3.3.1 膨胀过程
      • 3.3.2 获取重量级锁
      • 3.3.3 调用 EnterI 方法获得锁
  • 4. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档