博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jvm并发之锁类别
阅读量:5048 次
发布时间:2019-06-12

本文共 11485 字,大约阅读时间需要 38 分钟。

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
 
 
参考资料:
                  
                  (window的Mutex介绍)
                   

附件列表

 

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

你可能感兴趣的文章
学习python:day1
查看>>
css3动画属性
查看>>
第九次团队作业-测试报告与用户使用手册
查看>>
Equal Sides Of An Array
查看>>
CentOS笔记-用户和用户组管理
查看>>
Mongodb 基本命令
查看>>
Qt中QTableView中加入Check列实现
查看>>
“富豪相亲大会”究竟迷失了什么?
查看>>
控制文件的备份与恢复
查看>>
返回代码hdu 2054 A==B?
查看>>
Flink独立集群1
查看>>
iOS 8 地图
查看>>
20165235 第八周课下补做
查看>>
[leetcode] 1. Two Sum
查看>>
iOS 日常工作之常用宏定义大全
查看>>
PHP的SQL注入技术实现以及预防措施
查看>>
MVC Razor
查看>>
软件目录结构规范
查看>>
Windbg调试Sql Server 进程
查看>>
linux调度器系列
查看>>