Java多线程-对象内置锁(ObjectMonitor)

本文详细介绍了Java并发控制中Monitor的实现,包括ObjectMonitor的数据结构、对象模型、核心方法如TryLock、try_enter、enter、ExitI和exit。Monitor通过ObjectMonitor实现重量级锁,支持线程的等待、唤醒和锁的重入。文章还探讨了锁的获取、等待队列管理和退出策略,揭示了Java并发控制的底层机制。

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

1、介绍

Monitor是在JVM层对Java并发控制synchronized的重量级锁的实现。通过ObjectMonitor来实现并发的锁控制。同时也是Java基础对象Object的wait,nofity方法的底层支持实现。

2、对象模型

2.1 数据结构图

ObjectMonitor整体上可以分为两部分,一部分是是这个监控对象的基本信息表示当前锁的实时状态,一部分表示各种情况下需要获取锁的排队信息。如下图所示:

 

基本信息

1 header

用来保存锁对象的mark word的值。因为object里面已经不保存mark word的原来的值了,保存的是ObjectMonitor对象的地址信息。当所有线程都完成了之后,需要销毁掉ObjectMonitor的时候需要将原有的header里面的值重新复制到mark word中来。

2 object

指向的是对象的地址信息,方便通过ObjectMonitor来访问对应的锁对象。

3 owner

指向的是当前获得线程的地址,用来判断当前锁是被哪个线程持有。

排队信息

1cxq

线程刚进来的时候没有获取到锁的时候,在当前的排队队列。

2waitSet

是指已经获取得一次锁了,对象调用了wait方法,讲当前线程挂起了就进入了等待队列。等待时间到期的时候唤醒,或者其他线程唤醒。

3 entryList

是队列用来获取锁的缓冲区,用来将cxq和waitSet中的数据 移动到entryList进行排队。这个统一获取锁的入口。一般是cxq 或者waitSet数据复制过来进行统一排队。

2.2 ObjectMonitor 的源代码

ObjectMonitor() {
  _header       = NULL;
  _count        = 0;
  _waiters      = 0,
  _recursions   = 0;
  _object       = NULL;
  _owner        = NULL;
  _WaitSet      = NULL;
  _WaitSetLock  = 0 ;
  _Responsible  = NULL ;
  _succ         = NULL ;
  //多线程竞争锁进入时的单向链表
  _cxq          = NULL ;
  FreeNext      = NULL ;
  //_owner从该双向循环链表中唤醒线程结点,_EntryList是第一个节点
  _EntryList    = NULL ;
  _SpinFreq     = 0 ;
  _SpinClock    = 0 ;
  OwnerIsThread = 0 ;
  _previous_owner_tid = 0;
}

2.3 ObjectWaiter源代码

下面是排队对象ObjectWaiter的源代码的信息

class ObjectWaiter : public StackObj {
 public:
  enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ } ;
  enum Sorted  { PREPEND, APPEND, SORTED } ;
  ObjectWaiter * volatile _next;
  ObjectWaiter * volatile _prev;
  //等待的线程
  Thread*       _thread;
  jlong         _notifier_tid;
  ParkEvent *   _event;
  volatile int  _notified ;
  volatile TStates TState ;
  Sorted        _Sorted ;           // List placement disposition
  bool          _active ;           // Contention monitoring is enabled
};

3、核心方法

3.1 TryLock

int ObjectMonitor::TryLock (Thread * Self) {
   for (;;) {
      void * own = _owner ;
      //已经有线程获取到锁了 直接返回
      if (own != NULL){
          return 0 ;
       }
       //获取锁成功
      if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) {
         return 1 ;
      }
      if (true) return -1 ;
   }
}

通过上面代码我们可以详细的获取锁的逻辑,判断当前的监视器对象是否已经为空了,如果不为空说明已经有线程拿到锁了直接结束。当为空闲的尝试设置当前线程来获取锁,成功了直接返回1 。通过上面代码看总体上只做一次尝试,并不会多次尝试。

3.3 try_enter

try_enter 的方法是尝试获取当前锁对象 ,在已经拿过一次锁对象了之后,二次重新进入锁的场景使用。整个过程是只做一次尝试,不论成功失败都只执行一次。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值