jvm并发之锁类别

I、java同步锁
重量级锁具有很大互斥性,线程的堵塞和唤醒都需要从用户态Ring3到内核态Ring0,频繁的切换会加重cpu的负担。传统的synchronized属于重量级锁,其实现原理基于对Mutex锁的调用,在每个对象的对象头都有一个指向Monitor的指针,线程在执行同步代码块的时候,会先执行MonitorEnter,获取Monitor的锁,退出时执行MonitorExit,同步方法则在方法描述的flag中添加一个ACC_SYNCHRONIZED,原理和Monitor差不多。重量级锁的实现机制:每个线程都维护着一个私有的Minitor Record组,每一个被锁住的对象都会和一个monitor record相关联,而对象头中的LockWord会指向关联的monitorRecord的起始位置。monitor record的结构为如下:
     1140851-20170420125956759-62474218.png
 Owner:若为null,则表示没有线程占用锁,若不为空,则表示拥有改对象锁的线程的标识。
EntryQ:关联一个互斥锁,阻塞所有试图获得锁的线程。
Rcthis:阻塞的线程个数。
Nest:实现重入锁的计数。
Candidate:0表示没有需要唤醒的线程,1表示需要唤醒一个线程来竞争锁。

下图为java对象头中的markWord:
1140851-20170420125957274-1599598830.jpg
 从图可以看出一共有5种状态:
          001表示无锁状态,bitfields存储的是对象的hashCode,age等。
       101表示偏向锁,threadID初始值为null,当有线程获得锁时,其值就是线程的标识。
       00标识轻量级锁,其bitfields指向线程栈中的lock Record空间。
       10表示重量级互斥锁,bitfields指向monitor,11表示GC垃圾回收标识。

II、轻量级锁
轻量级锁的实现利用操作系统的CAS(compare and swap),避免去进入monitor,汇编执行如 CMPXCHG。
获取轻量锁过程:
                   判断对象是否处于无锁状态,若是则在线程栈中年创建锁记录,保存Object Header的markWord和指向Object Header的指针,并尝试通过CAS将锁记录的指针修改到ObjectHeader的markWord,若成功修改为00(轻锁)状态。
                如果对象处于被锁状态,CAS失败,判断Object的MarkWord中的指针是否指向当前线程的锁记录,若是则表明这是一个递归加锁,则初始化锁记录为0,而不是ObjectHeader的markWord。若不是膨胀为重量级锁,修改ObjectMarkword指向线程的monitor Record,并修改状态为10。
解锁过程:
                CAS替换MarkWord,成功则解锁成功。失败则表示有另外一个线程竞争,释放锁唤醒阻塞线程,对于已经膨胀为重量级锁,则不降级,即不会降级为轻量级锁。
 轻量级锁流程:
1140851-20170420125958915-90391956.png  分析在openJdk7中的代码:
        hotspot目录下的Synchronizer.cpp类中,轻量级进入同步如下:
             1140851-20170420125959524-1553307164.png
  1140851-20170420125959977-1893876224.png
  1140851-20170420130000337-756427140.png
下面是锁膨胀的过程:
 
    
  1. ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
  2. // Inflate mutates the heap ...
  3. // Relaxing assertion for bug 6320749.
  4. assert (Universe::verify_in_progress() ||
  5. !SafepointSynchronize::is_at_safepoint(), "invariant") ;
  6. for (;;) {
  7. //获取object的markWord
  8. const markOop mark = object->mark() ;
  9. assert (!mark->has_bias_pattern(), "invariant") ;
  10. // The mark can be in one of the following states:
  11. // * Inflated - just return
  12. // * Stack-locked - coerce it to inflated
  13. // * INFLATING - busy wait for conversion to complete
  14. // * Neutral - aggressively inflate the object.
  15. // * BIASED - Illegal. We should never see this
  16. // CASE: inflated
  17. if (mark->has_monitor()) {//若是已经膨胀为重量级锁,则返回
  18. ObjectMonitor * inf = mark->monitor() ;
  19. assert (inf->header()->is_neutral(), "invariant");
  20. assert (inf->object() == object, "invariant") ;
  21. assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid");
  22. return inf ;
  23. }
  24. // CASE: inflation in progress - inflating over a stack-lock.
  25. // Some other thread is converting from stack-locked to inflated.
  26. // Only that thread can complete inflation -- other threads must wait.
  27. // The INFLATING value is transient.
  28. // Currently, we spin/yield/park and poll the markword, waiting for inflation to finish.
  29. // We could always eliminate polling by parking the thread on some auxiliary list.
  30. if (mark == markOopDesc::INFLATING()) {//正在膨胀,并等待膨胀完毕
  31. TEVENT (Inflate: spin while INFLATING) ;
  32. ReadStableMark(object) ;
  33. continue ;
  34. }
  35. // CASE: stack-locked
  36. // Could be stack-locked either by this thread or by some other thread.
  37. //
  38. // Note that we allocate the objectmonitor speculatively, _before_ attempting
  39. // to install INFLATING into the mark word. We originally installed INFLATING,
  40. // allocated the objectmonitor, and then finally STed the address of the
  41. // objectmonitor into the mark. This was correct, but artificially lengthened
  42. // the interval in which INFLATED appeared in the mark, thus increasing
  43. // the odds of inflation contention.
  44. //
  45. // We now use per-thread private objectmonitor free lists.
  46. // These list are reprovisioned from the global free list outside the
  47. // critical INFLATING...ST interval. A thread can transfer
  48. // multiple objectmonitors en-mass from the global free list to its local free list.
  49. // This reduces coherency traffic and lock contention on the global free list.
  50. // Using such local free lists, it doesn't matter if the omAlloc() call appears
  51. // before or after the CAS(INFLATING) operation.
  52. // See the comments in omAlloc().
  53. //若没有线程膨胀锁,线程开始膨胀锁
  54. if (mark->has_locker()) {//若有线程锁,证明存在竞争线程
  55. ObjectMonitor * m = omAlloc (Self) ;//从线程local中omInFreeList获取到一个可用的monitor,若omInFreeList没有则从全局gFreeList中获取,再没有的话new ObjectMonitor。
  56. // Optimistically prepare the objectmonitor - anticipate successful CAS
  57. // We do this before the CAS in order to minimize the length of time
  58. // in which INFLATING appears in the mark.
  59. m->Recycle();
  60. m->_Responsible = NULL ;
  61. m->OwnerIsThread = 0 ;
  62. m->_recursions = 0 ;
  63. m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ; // Consider: maintain by type/class
  64. //CAS修改markWord(0值),修改为正在膨胀状态
  65. markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;
  66. if (cmp != mark) {//失败再次尝试
  67. //将monitor放回线程的omInFreeList
  68. omRelease (Self, m, true) ;
  69. continue ; // Interference -- just retry
  70. }
  71. // We've successfully installed INFLATING (0) into the mark-word.
  72. // This is the only case where 0 will appear in a mark-work.
  73. // Only the singular thread that successfully swings the mark-word
  74. // to 0 can perform (or more precisely, complete) inflation.
  75. //
  76. // Why do we CAS a 0 into the mark-word instead of just CASing the
  77. // mark-word from the stack-locked value directly to the new inflated state?
  78. // Consider what happens when a thread unlocks a stack-locked object.
  79. // It attempts to use CAS to swing the displaced header value from the
  80. // on-stack basiclock back into the object header. Recall also that the
  81. // header value (hashcode, etc) can reside in (a) the object header, or
  82. // (b) a displaced header associated with the stack-lock, or (c) a displaced
  83. // header in an objectMonitor. The inflate() routine must copy the header
  84. // value from the basiclock on the owner's stack to the objectMonitor, all
  85. // the while preserving the hashCode stability invariants. If the owner
  86. // decides to release the lock while the value is 0, the unlock will fail
  87. // and control will eventually pass from slow_exit() to inflate. The owner
  88. // will then spin, waiting for the 0 value to disappear. Put another way,
  89. // the 0 causes the owner to stall if the owner happens to try to
  90. // drop the lock (restoring the header from the basiclock to the object)
  91. // while inflation is in-progress. This protocol avoids races that might
  92. // would otherwise permit hashCode values to change or "flicker" for an object.
  93. // Critically, while object->mark is 0 mark->displaced_mark_helper() is stable.
  94. // 0 serves as a "BUSY" inflate-in-progress indicator.
  95. // fetch the displaced mark from the owner's stack.
  96. // The owner can't die or unwind past the lock while our INFLATING
  97. // object is in the mark. Furthermore the owner can't complete
  98. // an unlock on the object, either.
  99. markOop dmw = mark->displaced_mark_helper() ;//获取displaced_mark(hashcode,age)
  100. assert (dmw->is_neutral(), "invariant") ;
  101. // Setup monitor fields to proper values -- prepare the monitor
  102. m->set_header(dmw) ;//monitor存储displaced_mark_word
  103. // Optimization: if the mark->locker stack address is associated
  104. // with this thread we could simply set m->_owner = Self and
  105. // m->OwnerIsThread = 1. Note that a thread can inflate an object
  106. // that it has stack-locked -- as might happen in wait() -- directly
  107. // with CAS. That is, we can avoid the xchg-NULL .... ST idiom.
  108. m->set_owner(mark->locker());//设置monitor的拥有者
  109. m->set_object(object);
  110. // TODO-FIXME: assert BasicLock->dhw != 0.
  111. // Must preserve store ordering. The monitor state must
  112. // be stable at the time of publishing the monitor address.
  113. guarantee (object->mark() == markOopDesc::INFLATING(), "invariant") ;
  114. object->release_set_mark(markOopDesc::encode(m));//markWord设置为重量级锁ptr|10
  115. // Hopefully the performance counters are allocated on distinct cache lines
  116. // to avoid false sharing on MP systems ...
  117. if (ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;
  118. TEVENT(Inflate: overwrite stacklock) ;
  119. if (TraceMonitorInflation) {
  120. if (object->is_instance()) {
  121. ResourceMark rm;
  122. tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
  123. (intptr_t) object, (intptr_t) object->mark(),
  124. Klass::cast(object->klass())->external_name());
  125. }
  126. }
  127. return m ;
  128. }
  129. // CASE: neutral
  130. // TODO-FIXME: for entry we currently inflate and then try to CAS _owner.
  131. // If we know we're inflating for entry it's better to inflate by swinging a
  132. // pre-locked objectMonitor pointer into the object header. A successful
  133. // CAS inflates the object *and* confers ownership to the inflating thread.
  134. // In the current implementation we use a 2-step mechanism where we CAS()
  135. // to inflate and then CAS() again to try to swing _owner from NULL to Self.
  136. // An inflateTry() method that we could call from fast_enter() and slow_enter()
  137. // would be useful.
  138. //线程2在开始膨胀锁时,线程1已经解锁的情况下,创建膨胀锁
  139. assert (mark->is_neutral(), "invariant");
  140. ObjectMonitor * m = omAlloc (Self) ;
  141. // prepare m for installation - set monitor to initial state
  142. m->Recycle();
  143. m->set_header(mark);
  144. m->set_owner(NULL);
  145. m->set_object(object);
  146. m->OwnerIsThread = 1 ;
  147. m->_recursions = 0 ;
  148. m->_Responsible = NULL ;
  149. m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ; // consider: keep metastats by type/class
  150. //CAS monitor
  151. if (Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
  152. m->set_object (NULL) ;
  153. m->set_owner (NULL) ;
  154. m->OwnerIsThread = 0 ;
  155. m->Recycle() ;
  156. omRelease (Self, m, true) ;
  157. m = NULL ;
  158. continue ;
  159. // interference - the markword changed - just retry.
  160. // The state-transitions are one-way, so there's no chance of
  161. // live-lock -- "Inflated" is an absorbing state.
  162. }
  163. // Hopefully the performance counters are allocated on distinct
  164. // cache lines to avoid false sharing on MP systems ...
  165. if (ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;
  166. TEVENT(Inflate: overwrite neutral) ;
  167. if (TraceMonitorInflation) {
  168. if (object->is_instance()) {
  169. ResourceMark rm;
  170. tty->print_cr("Inflating object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
  171. (intptr_t) object, (intptr_t) object->mark(),
  172. Klass::cast(object->klass())->external_name());
  173. }
  174. }
  175. return m ;
  176. }
  177. }


 锁膨胀过程:
         1140851-20170420130001446-1666703889.jpg
 


 退出锁:
     1140851-20170420130001712-1161512372.png
 
 
   
  1. void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
  2. assert(!object->mark()->has_bias_pattern(), "should not see bias pattern here");
  3. // if displaced header is null, the previous enter is recursive enter, no-op
  4. markOop dhw = lock->displaced_header();
  5. markOop mark ;
  6. if (dhw == NULL) { //递归加锁的在slowEnter里已经把lockRecord设置为null
  7. // Recursive stack-lock.
  8. // Diagnostics -- Could be: stack-locked, inflating, inflated.
  9. mark = object->mark() ;//获取object的markWord
  10. assert (!mark->is_neutral(), "invariant") ;
  11. if (mark->has_locker() && mark != markOopDesc::INFLATING()) {//判断是否有lock并且当前锁是否正在膨胀
  12. assert(THREAD->is_lock_owned((address)mark->locker()), "invariant") ;
  13. }
  14. if (mark->has_monitor()) {//判断markRecord是否有monitor重量级锁
  15. ObjectMonitor * m = mark->monitor() ;
  16. assert(((oop)(m->object()))->mark() == mark, "invariant") ;
  17. assert(m->is_entered(THREAD), "invariant") ;//
  18. }
  19. return ;
  20. }
  21. mark = object->mark() ;
  22. // If the object is stack-locked by the current thread, try to
  23. // swing the displaced header from the box back to the mark.
  24. //不存在线程竞争,线程CAS将lockRecord和object的markWord交换
  25. if (mark == (markOop) lock) {
  26. assert (dhw->is_neutral(), "invariant") ;
  27. if ((markOop) Atomic::cmpxchg_ptr (dhw, object->mark_addr(), mark) == mark) {
  28. TEVENT (fast_exit: release stacklock) ;
  29. return;
  30. }
  31. }
  32. ObjectSynchronizer::inflate(THREAD, object)->exit (THREAD) ;//释放锁
  33. }



III、偏向锁
偏向锁:与轻量级锁比较,因为cas操作任然存在的一定的开销,故出现了偏向锁。只有第一个线程CAS成功才能把线程id假如到markWord中,这时候可以叫该对象偏向于该线程。当该线程再次去获得锁时不需要执行CAS操作更新markWork,虽然堆栈上的锁记录未被初始化,但其对于对象是偏向的,故不需要校验。当存在另外一个线程在该对象同步时,需要撤销偏向锁,到达安全点(safePoint)时(线程暂停)遍历获得偏向锁的线程堆栈,调整锁记录和Object的markWord关联(轻量级锁)。解锁过程如轻量级锁。
            
1140851-20170420130002665-1162424194.jpg

各种锁的状态转换图:
  1140851-20170420130003368-1689573491.png
 


 
                  http://blog.csdn.net/hffhjh111/article/details/53140909(window的Mutex介绍)




附件列表

 

转载于:https://www.cnblogs.com/zhenyimo/p/6738210.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值