java并发之彻底搞懂偏向锁升级为轻量级锁

甜菜波波      2022-04-06     423

关键词:

网上有许多讲偏向锁,轻量级锁的文章,但对偏向锁如何升级讲的不够明白,有些文章还相互矛盾,经过对jvm源码(biasedLocking.cpp)的仔细分析和追踪,基本升级过程有了一个清晰的过程,现将升级流程阐述如下:

     因为偏向锁,锁住对象时,会写入对象头相应的标识,我们先把对象头(官方叫法为:Mark Word)的图示如下(借用了网友的图片):

      技术分享图片

    通过上面的图片,我们可以知道,对象处于偏向锁时,mark word中的偏向锁标记为1,锁标志位为01;下面是分析过jvm源码(biasedLocking.cpp)解析的偏向锁升级流程(忽略一些细节),示例中:线程1当前拥有偏向锁对象,线程2是需要竞争到偏向锁。

  1. 线程2来竞争锁对象;
  2. 判断当前对象头是否是偏向锁;
  3. 判断拥有偏向锁的线程1是否还存在;
  4. 线程1不存在,直接设置偏向锁标识为0(线程1执行完毕后,不会主动去释放偏向锁);
  5. 使用cas替换偏向锁线程ID为线程2,锁不升级,仍为偏向锁;
  6. 线程1仍然存在,暂停线程1;
  7. 设置锁标志位为00(变为轻量级锁),偏向锁为0;
  8. 从线程1的空闲monitor record中读取一条,放至线程1的当前monitor record中;
  9. 更新mark word,将mark word指向线程1中monitor record的指针;
  10. 继续执行线程1的代码;
  11. 锁升级为轻量级锁;   
  12. 线程2自旋来获取锁对象;
   上面仍有一个问题,即如何判断线程1已经不存在了?
        仍然是分析完jvm源码(thread.cpp)后,得到的如下结论:
           (1) 线程执行start时,会将自己写入一个thread_list中,这是一个linked结构,有pre和next节点;
                        对应源码位置:
                               void Threads::add(JavaThread* p, bool force_daemon) {
  // The threads lock must be owned at this point
  assert_locked_or_safepoint(Threads_lock);
  // See the comment for this method in thread.hpp for its purpose and
  // why it is called here.
  p->initialize_queues();
  p->set_next(_thread_list);
  _thread_list = p;
  _number_of_threads++;
  oop threadObj = p->threadObj();
  bool daemon = true;
  // Bootstrapping problem: threadObj can be null for initial
  // JavaThread (or for threads attached via JNI)
  if ((!force_daemon) && (threadObj == NULL || !java_lang_Thread::is_daemon(threadObj))) {
        _number_of_non_daemon_threads++;
        daemon = false;
  }
  p->set_safepoint_visible(true);
  ThreadService::add_thread(p, daemon);
    // Possible GC point.
  Events::log(p, "Thread added: " INTPTR_FORMAT, p);
   }
           (2)线程执行完后,会将自己从thread list中清理掉(源码位置: Threads::remove(this));
       因此只需判断thread list中是否存在线程1即可,判断源代码(位于biasedLocking.cpp中 )如下:
             bool thread_is_alive = false;
 if (requesting_thread == biased_thread) {
       thread_is_alive = true;
 } else {
     for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) {
              if (cur_thread == biased_thread) {
                    thread_is_alive = true;
                    break;
              }
          }

java并发编程线程锁机制(锁的四种状态|无锁状态|偏向锁|轻量级锁|重量级锁|锁竞争|锁升级)(代码片段)

...ck)二、重量级锁弊端三、锁的四种状态(无锁状态|偏向锁|轻量级锁|重量级锁)四、锁的四种状态之间的转换(无锁状态->偏向锁->轻量级锁->重量级锁)一、悲观锁示例(ReentrantLock)ReentrantLock与synchronized都是悲观锁;ReentrantLock是Lo... 查看详情

synchronized的实现原理以及锁升级详解

...向锁​​​​偏向锁的撤销​​​​关闭偏向锁​​​​轻量级锁​​​​轻量级锁加锁​​​​轻量级锁解锁​​​​锁的优缺点对比​​​​锁的验证​​​​引入jar包​​​​无锁​​​​偏向锁​​​​轻量级锁​​​... 查看详情

java并发编程:synchronized底层优化(偏向锁轻量级锁)

...ynchronized及其实现原理Java并发编程:Synchronized底层优化(轻量级锁、偏向锁)Java并发编程:线程间的协作(wait/notify/sleep/yield/join)Java并发编程:volatile的使用及其原理一、重量级锁  上篇文章中向大家介绍了Synchronized的用法及... 查看详情

偏向锁理论太抽象,实战了解下偏向锁如何发生以及如何升级实战篇(代码片段)

锁升级上文我们主要介绍什么是偏向锁,轻量级锁,重量级锁。并分析了三者的区别和使用场景。还记得Redis章节中整数集中升级操作吗。在锁中我们同样是设计锁升级和降级的。上文我们也介绍了当没有竞争时偏向锁&#x... 查看详情

偏向锁理论太抽象,实战了解下偏向锁如何发生以及如何升级实战篇(代码片段)

锁升级上文我们主要介绍什么是偏向锁,轻量级锁,重量级锁。并分析了三者的区别和使用场景。还记得Redis章节中整数集中升级操作吗。在锁中我们同样是设计锁升级和降级的。上文我们也介绍了当没有竞争时偏向锁&#x... 查看详情

偏向锁理论太抽象,实战了解下偏向锁如何发生以及如何升级实战篇(代码片段)

锁升级上文我们主要介绍什么是偏向锁,轻量级锁,重量级锁。并分析了三者的区别和使用场景。还记得Redis章节中整数集中升级操作吗。在锁中我们同样是设计锁升级和降级的。上文我们也介绍了当没有竞争时偏向锁&#x... 查看详情

java并发专题之三java线程同步

...,还增加了自适应的CAS自旋、锁消除、锁粗化、偏向锁、轻量级锁这些优化策略。  优化后的锁有四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量 查看详情

synchronized底层实现

...果成功则获得偏向锁,如果不成功则说明有竞争,升级为轻量级锁。后续再通过CAS将线程的指针放到markword中,若成功则获得锁,否则升级为自旋锁。自旋锁仍然为轻量级锁,不成功升级为重量级锁。  对象结构:在JVM中... 查看详情

多线程高并发之synchronized锁及其膨胀

...Java8所使用的synchronized锁是经过优化后的,存在偏向锁、轻量级锁、重量级锁等状态。线程间不存在锁的竞争行为,至多只有一个线程有获取锁的需求,常见场景为单线程程序。判断是不是偏向锁的标识是查看调用此方法的线程... 查看详情

面试java基础锁

...程访问,那个该线程会自动获取锁,降低获取锁的代价。轻量级锁是指当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获 查看详情

java-锁与实现

...锁的时候,在进入内层方法会自动获取锁。  偏向锁/轻量级锁/重量级锁:  偏向锁:指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。  轻量级锁:指当锁是偏向锁的时候,被另一... 查看详情

synchronized原理及1.6之后的锁升级优化

参考技术A偏向所锁,轻量级锁及重量级锁偏向所锁,轻量级锁都是乐观锁,重量级锁是悲观锁。一个对象刚开始实例化的时候,没有任何线程来访问它的时候。它是可偏向的,意味着,它现在认为只可能有一个线程来访问它,... 查看详情

并发编程——synchronized优化原理

...带来的性能消耗,在jdk6之后便引入了“偏向锁”和“轻量级锁”,所以Java中的锁总共有4种锁状态,级别由低到高依次为:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。它会随着竞争情况逐渐升级。锁... 查看详情

java6中对synchronized的优化

...类锁的升级(进化)偏向锁偏向锁的加锁偏向锁的撤销总结轻量级锁轻量级锁加锁轻量级锁解锁总结锁的比较总结1.概述在多线程并发编程中synchronized一直是元老级角色,很多人都会称呼它为重量级锁.但是,随着JavaSE1.6对synchronized进... 查看详情

synchronized锁性能优化偏向锁轻量级锁升级多线程中篇

...在现在的版本中,锁的状态总共有四种:无锁状态偏向锁轻量级锁重量级锁很显然锁的“重量”从左到右,依次递增无所状态很好理解,新增加的偏向锁与轻量级锁,其实就是尽可能的将重量级锁往“无锁”的方向... 查看详情

锁升级

...获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”。在JavaSE1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可... 查看详情

2.偏向锁,轻量锁,重量锁

...线程交替执行,无竞争 轻量锁与偏向锁不同的是:轻量级锁每次退出同步块都需要释放锁,而偏向锁是在竞争发生时才释放锁每次进入退出同步块都需要CAS更新对象头争夺轻量级锁失败时,自旋尝试抢占锁 可以看到轻... 查看详情

偏向锁

偏向锁轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行CAS操作Java6中引入了偏向锁来作进一步优化!只有第一次使用CAS将线程ID设置到对象的MarkWord头,之后发现这个线程ID是自己就表示没有竞争,不用重新CAS.回... 查看详情