Java Synchronized的偏向锁详细分析

synchronized作为Java程序员最常用同步工具,很多人却对它的用法和实现原理一知半解,以至于还有不少人认为synchronized是重量级锁,性能较差,尽量少用。但不可否认的是synchronized依然是并发首选工具,本文就来详细讲讲

上篇文章已经对Synchronized关键字做了初步的介绍,从字节码层面介绍了Synchronized关键字,最终字节码层面就是monitorenter和monitorexit字节码指令。并且拿Synchronized关键字和Java的JUC包下的ReentrantLock做了比较。Synchronized关键字的初体验-超链接地址

那么本篇文章将开始深入解析Synchronized关键字的底层原理,也就是解析Hotspot虚拟机对monitorenter和monitorexit字节码指令的实现原理。

理论知识

相信各位读者在准备面试中,都会背到关于Synchronized关键字的面试题,什么对象头、锁标志位、偏向锁、轻量级锁、重量级锁,锁升级的过程等等面试题。而对于一些不仅仅只想漂浮于表面的读者来说,去看Synchronized底层源码,只能说是一头雾水。所以笔者有考虑这方面,所以理论知识(给临时抱佛脚背理论的读者)和底层源码(给喜欢研究底层源码的读者)都会在这个系列中。

偏向锁存在的意义:

先从字面意思来解释,偏向于某个线程,是不是可以理解为偏向的这个线程获取锁都很效率呢?那么为什么要存在偏向锁呢?读者需要明白,任何框架存在的意义不仅仅是为了某一部分场景,肯定需要适配大部分场景,而Synchronized关键字使用的场景可能并发高,可能并发低,可能几乎不存在并发,所以实现者需要帮用户去适配不同的场景,达到效率最高化。而对于几乎不存在并发的场景,是不是可以理解为几乎只有一个线程拿到Synchronized锁,所以就存在偏向锁去优化这种场景,不让所有场景都去走很复杂的逻辑。

偏向锁实现的流程:

  • 拿到锁竞争对象
  • 从当前线程栈中获取到一个没有使用的BasicObjectLock(用于记录锁状态)
  • 查看当前是否开启了偏向锁模式
  • 查看当前偏向锁是否偏向的是当前线程,如果偏向的是当前线程,直接退出(可以理解成命中缓存)
  • 查看当前是否已经锁升级了,并且尝试撤销偏向锁(想象一下并发过程中,可能其他线程已经完成了锁对象的锁升级)
  • 当前epoch是否发生了改变,如果发生了改变,当前线程可以尝试获取偏向锁,尝试成功直接退出
  • 当前是否是匿名偏向,或者已经偏向于某个线程,但是不是当前线程,此时可以尝试获取锁,获取成功直接退出
  • 如果不支持偏向锁或者第5步的撤销偏向锁失败了,此时尝试膨胀成轻量级锁,如果轻量级锁膨胀失败了就继续往上锁膨胀

流程图如下(仅只有偏向锁逻辑)

源码论证

首先,我们先需要知道Synchronized底层源码的入口在哪里,在字节码层面表示为monitorenter和monitorexit字节码指令,而我们知道JVM是负责执行字节码,最终转换成不同CPU平台的ISA指令集(也称之为跨平台)。而JVM执行字节码分为

  • CPP解释执行
  • 模板解释执行(汇编)
  • JIT编译执行

一级一级的优化,而最根本是CPP解释执行,后者都是基于CPP解释执行的不断优化,后者的难度极大,所以读者弄明白CPP解释执行就即可。

在Hotspot源码中,CPP解释执行的入口在bytecodeInterpreter.cpp文件(这里要注意,JDK1.8不同版本对synchronized关键字实现有区别,所以本文选的是jdk8u40版本,其他版本可能没有偏向锁等等逻辑)

首先,读者明白,使用Synchronized关键字时需要一个锁对象,而底层就是操作这个锁对象的对象头,所以我们先从markOop.hpp文件中找到对象头的描述信息,是不是跟外面8股文描述的一模一样呢

以上就是Java Synchronized的偏向锁详细分析的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » Java