jdk-synchronized源码学习

news2025/2/26 19:54:30

synchronized介绍

    java中jdk1.6之前和jdk1.6及之后synchronized完全不一样。1.6之前直接上来都是重量级锁导致java的性能很低效,而1.6及之后甲骨文公司对其进行优化,通过一个锁的升级过程从而来支持一些非复杂的场景。那么本文主要是针对synchronized的源码及一些使用进行了解。

java的内存布局和对象头及锁升级过程

请查看之前发布过的文章:对象实例化与内存布局(深入)

加锁的三种方式及锁的粒度

加到静态方法,锁是当前类对象

public synchronized static  void method(){
    System.out.println("锁的是对象");
}

同步类方法,锁是当前方法

public synchronized void method2(){
    System.out.println("锁的是方法!");
}

同步代码块,锁是括号里面的对象

public  void method1(){
    synchronized (this){
        System.out.println("锁是代码块!");
    }
}

查看锁

public class SynchronizedLock {
    public synchronized void method(){
        System.out.println("同步锁测试!");
    }
}

javap -c -v  SynchronizedLock.class

Classfile /Users/csh/ideaworkspace/jdk8/src/main/java/com/lock/SynchronizedLock.class
  Last modified 2022-11-20; size 512 bytes
  MD5 checksum c1a83fca01614297b7553ecf31e0b979
  Compiled from "SynchronizedLock.java"
public class com.lock.SynchronizedLock
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref #7.#16 // java/lang/Object."<init>":()V
   #2 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String #19 // 同步锁测试!
   #4 = Methodref #20.#21 // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = String #22 // 普通方法!
   #6 = Class #23 // com/lock/SynchronizedLock
   #7 = Class #24 // java/lang/Object
   #8 = Utf8 <init>
   #9 = Utf8 ()V
  #10 = Utf8 Code
  #11 = Utf8 LineNumberTable
  #12 = Utf8 method
  #13 = Utf8 method2
  #14 = Utf8 SourceFile
  #15 = Utf8 SynchronizedLock.java
  #16 = NameAndType #8:#9 // "<init>":()V
  #17 = Class #25 // java/lang/System
  #18 = NameAndType #26:#27 // out:Ljava/io/PrintStream;
  #19 = Utf8 同步锁测试!
  #20 = Class #28 // java/io/PrintStream
  #21 = NameAndType #29:#30 // println:(Ljava/lang/String;)V
  #22 = Utf8 普通方法!
  #23 = Utf8 com/lock/SynchronizedLock
  #24 = Utf8 java/lang/Object
  #25 = Utf8 java/lang/System
  #26 = Utf8 out
  #27 = Utf8 Ljava/io/PrintStream;
  #28 = Utf8 java/io/PrintStream
  #29 = Utf8 println
  #30 = Utf8 (Ljava/lang/String;)V
{
  public com.lock.SynchronizedLock();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1 // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0

  public synchronized void method();
    descriptor: ()V
    //注意:ACC_SYNCHRONIZED 是用来标识是同步方法
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc #3 // String 同步锁测试!
         5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 10: 0
        line 11: 8

  public void method2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc #5 // String 普通方法!
         5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 14: 0
        line 15: 8
}

ad84e0d1d5f0e4a04c0fcb30e1f7f0bd.png

从上面可以看出来普通的方法和同步方法,在字节码层面同步方法多了一个ACC_SYNCHRONIZED 从而来使jvm知道这个方法是同步方法。

什么是monitor对象?

f1fce4e6bb05455734fe392a849b4f96.png

首先monitor叫作管程或监视器,用于创建监视锁,每个对象都存在一个monitor关联的对象,包含monitorenter和monitorexit。

个人理解:这个就是监控,你干啥干啥了。

流程是这样的:

e13396df099db106ce7632a9cd71fa57.png

    如果当进入的时候monitor为0,则代表没有被持有,会将这个monitor计数器设置为1;

    如果进来的时候线程为该monitor的持有者,那么计数器加1(可重入);

    如果该线程不是该monitor的持有者,进来获取若已被其它线程持有,那么获取monitor失败,进入阻塞状态,直到monitor计数器为0,其它线程退出Monitor,重新唤醒同步队列中的线程;

为什么会用两个MonitorExit指令?

    第一个退出指令为正常退出,第二个为异常退出。

源码阅读

ObjectMonitor.hpp(C++代码)

ObjectMonitor() {
   _header = NULL; //头部默认为空
   _count = 0; //记录个数
   _waiters = 0, //等待中的线程数
   _recursions = 0; //线程重入次数
   _object = NULL; //存储该monitor的对象
   _owner = NULL; //拥有该monitor的线程
   _WaitSet = NULL; //等待线程的列表
   _WaitSetLock = 0 ; 
   _Responsible = NULL ;
   _succ = NULL ;
   _cxq = NULL ; //多线程竟争锁时的单向链表
   FreeNext = NULL ;
   _EntryList = NULL ; //待锁列表(入口列表)
   _SpinFreq = 0 ;
   _SpinClock = 0 ;
   OwnerIsThread = 0 ;
   _previous_owner_tid = 0; // 前一个拥有此监视器的线程 ID
 }

注意这里:_WaitSet和_EntryList是ObjectMonitor的两个主要队列,分别用来存放等待队列和正在执行的集合。

多线程时的执行流程:

先进入_EntryList集合,然后线程去获取monitor,若成功获取则进入_Owner并把当前线程设置为owner,同时Monitor中的计数器count加1(_count);

若线程执行过程中调用了wait()方法,当前线程就会释放持有的monitor,这时_owner重新被置为null,count减1,同时该线程被放入_WaitSet列表中等待被唤醒。

若线程执行完毕,那么默认会释放monitor(锁)并将count的值减1或置为0,让出这个锁并且唤醒_WaitSet队列中的线程。

hostpot中有两种解释器模板解释器和字节码解释器,其中字节码是基于汇偏于言,虚拟机早期开发的一种解释器(很慢的原因,一行一行读的),而模板是正在正在解释使用的另外一种高效快速解释器。(不懂解释器请看之前文章!)

不过模板解释器基于汇编来写的(性能高啊~)比较难读,而字节码解释器是C++代码写的。

以下来源于:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/interpreter/bytecodeInterpreter.cpp#l1816

实际不会读到这个bytecodeInterpreter.cpp而是读到templateTable_x86_64.cpp

395827b60952ed7584a3f5dd1ddd4baa.png

87c624b1dcfc401e2f69fbfb876e36c1.png

以下代码位置:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/cpu/x86/vm/templateTable_x86_64.cpp#l3667

8154344faab51cb046f894d67c635baa.png

1bdeadff92f2b32b4209d6654e30bc8d.png

这个基于汇编语言的,的确纯阅读起来,太费解了,还是需要一行一行来调试会更易理解,想想这个学汇编都十几年前的事了~,还是建议看看c++吧。

最后

    由于时间问题及考虑到把所有c++或汇编源码贴出来没什么意思,建议有需要深入了解同步锁,锁升级过程的源码同学可以看看下面的参考文章,里面有些大佬已经把详细步骤及过程写出来了,可以深入再学学。关于虚拟机的汇编源码我本人是不建议看,因为代码量巨大还有耗费的时间可能需要最少长达半个月或几个月才能消化完,所以有这个时间都可以学一个新的技术点了,当然有需要深入或工作上需要的同学那建议看看下面的参考文章可以减少不少的学习弯路。

参考文章:

https://github.com/farmerjohngit/myblog/issues/13

https://blog.csdn.net/u014454538/article/details/120731549

https://tech.meituan.com/2018/11/15/java-lock.html

https://www.pdai.tech/md/java/thread/java-thread-x-key-synchronized.html

https://xie.infoq.cn/article/3ac3596347f9e914a2d2a3587

https://xiaomi-info.github.io/2020/03/24/synchronized/

https://www.cnblogs.com/trunks2008/p/14613090.html

https://xie.infoq.cn/article/3ac3596347f9e914a2d2a3587

https://zhuanlan.zhihu.com/p/33830504

c++实现源码类:

http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/interpreter/bytecodeInterpreter.cpp

http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/cpu/x86/vm/templateTable_x86_64.cpp

http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/interpreter/interpreterRuntime.cpp

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/39410.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java并发-ThreadLocal的使用

ThreadLocal 概述 当使用 ThreadLocal 维护变量时&#xff0c;ThreadLocal 为每个使用该变量的线程提供独立的变量副本&#xff0c;所以每一个线程都可以独立地改变自己的副本&#xff0c;而不会影响其它线程所对应的副本。当多个线程操作这个变量时&#xff0c;实际操作的是自…

python实现熵权法

原文&#xff1a;https://mp.weixin.qq.com/s/vPNPdbZy11q1qsfEz9etZQ 1 熵权法简介 熵源自于希腊语 “ 变化 ” 表示变化的容量&#xff0c;德国物理学家克劳修斯为了将热力学第二定律格式化而引入熵的概念 。 熵的概念来源于热力学&#xff0c;是用来描述过程的不可逆现象…

sql注入手法详解

sql定义 sql--结构化查询语句 sql注入&#xff1a;首先我们通过前端将我们的payload(恶意代码)传送到后台服务器 传送到后台以后 我们提交的payload拼接到sql语句中 作为sql语句的一部分被执行 从而导致数据库又被脱库甚至删库的风险 使得数据库受损 sql注入手法 sql注入可…

领悟《信号与系统》之 非周期信号的傅里叶变换及性质

非周期信号的傅里叶变换及性质一、非周期信号的傅里叶变换二、 典型信号的傅立叶变换1.单边指数信号2.偶双边指数3. 矩阵脉冲信号4. 奇双边指数5. 符号函数6. 冲激信号7. 阶跃信号三、常用傅里叶变换表这里记录的信号都是非周期信号的傅里叶变化&#xff0c;频谱变换的特点就是…

VsCode 配置eslint,支持typescript的语法检查,及时发现低级语法错误,包括函数未定义等行为

背景 最近学习cocos的小游戏制作&#xff0c;参考游戏管理器的代码进行调试的时候&#xff0c;发现自己运行的结果一直都是跟官方效果不一样&#xff0c;没有自动生成更多的方块。肉眼检查代码差异&#xff0c;基本上代码是一样的&#xff0c;浏览器页面调试的时候也看不出问题…

[附源码]Python计算机毕业设计高校教室申请管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【仿牛客网笔记】项目进阶,构建安全高效的企业服务——热帖排行

p:投票数 T&#xff1a;发布时间间隔 G&#xff1a;系数&#xff0c;通常为1.5,1.8 计算帖子的分数 注入RedisTemplate 帖子刷新 实现定时任务 刷新帖子 实现更新帖子分数 刷新帖子分数任务 配置Trigger 注释掉定时任务&#xff0c;注释注解就可以 启动服务之…

人口数据集:地级市常住人口与户籍人口、人口1%抽样调查数据两大维度指标数据

一、地级市常住人口与户籍人口 1、数据来源&#xff1a;地级市常住人口数据&#xff08;主要来源于各地政府公报&#xff09;&#xff0c;户籍人口数据来源于《中国城市统计年鉴》 2、时间跨度&#xff1a;2003-2019年 3、区域范围&#xff1a;280个地级市 4、指标说明&…

[附源码]Python计算机毕业设计电影网站系统设计

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【Spring】Bean生命周期

一、背景&#xff1a; 自动注入 UserService 对象&#xff0c; UserService 结构如下 二、创建 Bean 的整体流程&#xff1a; UserService.class ------> 无参的构造方法 ------> 普通对象&#xff08;无值&#xff09; ------> 依赖注入 ------> 初始化前&#…

FreeCAD二次开发-基于控制台模式FC外部开发

版本 FreeCAD0.18.2+PyCharm Community 2020.3.3 演示效果 环境搭建步骤 1.先安装好FreeCAD和PyCharm 2.添加环境变量 点击确定,全部关掉。 3.测试变量是否生效(CMD打开控制台,输入python回车) 弹出如下,说明可以进入FreeCAD自带的python解释器 4.打开PyCharm新建项目 …

Windows静态库用C++代码调用C语言的旧库方法extern ”c“

静态库特点 运行不存在 静态库源码被链接到调用程序中 目标程序的归档 C语言静态库 C静态库的创建 1.创建一个静态库项目 2.添加库程序&#xff0c;源文件使用C文件 C静态库的使用 库路径设置&#xff1a;可以使用pragma关键字设置 #pragma comment&#xff08;lib,“./lib/cli…

15、Mybatis获取参数值的情况1(mapper接口方法的参数为单个字面量类型)

Mybatis获取参数值的情况1&#xff08;mapper接口方法的参数为单个字面量类型&#xff09; #{}和${}可以通过任意名称来获取 mapper接口方法的参数为单个字面量类型第一步&#xff1a; 第二步&#xff1a;#{} 结果&#xff1a; 第二步:${} 结果 第三步&#xff1a; 这种也行&…

站酷基于服务网格 ASM 的生产实践

01背景介绍Aliware站酷&#xff08;ZCOOL&#xff09;2006 年 8 月创立于北京&#xff0c;深耕设计领域多年&#xff0c;聚集了 1500 万设计师、摄影师、插画师、艺术家、创意人&#xff0c;在设计创意群体中具有一定的影响力与号召力。站酷在创立之初&#xff0c;就以“让设计…

libcurl 库的编译

因为要用到 libcurl 库的接口进行练习&#xff0c;而现在手上只有curl相关的头文件&#xff0c;那没办法只能下载源码进行编译了&#xff0c;这里记录一下编译 x86 与 hisi dv300 版本的动态库。 根据头文件 curlver.h 里的版本信息&#xff0c;我是直接下载 7.67.0 版本的源码…

领悟《信号与系统》之 LTI 系统的卷积积分及性质

LTI 系统的卷积积分及性质一、卷积积分及其性质1. 任意信号的分解2. 任意信号作用下的零状态响应3. 卷积的定义4. 卷积性质1. 代数性质2. f(t)与奇异信号的卷积 &#xff08;非常重要&#xff09;3. 卷积的微分和积分4. 卷积时移5. 常用信号的卷积公式二、总结重点笔记主要记录…

MNN--初步学习

来自阿里MNN有三个贡献点&#xff1a; 提出了预推理机制&#xff0c;在线计算推理成本和最优方案优化了kernel提出后端抽象实现混合调度MNN的架构&#xff1a; 分离线和在线两部分。离线就是很传统的模型转换、优化、压缩、量化的那一套东西&#xff0c;这里mnn转出的模型文件…

【外卖项目实战开发三】

文章目录分类管理业务开发公共字段自动填充问题分析代码实现功能完善新增分类需求分析数据模型代码开发分类信息分页查询需求分析代码开发删除分类需求分析代码开发代码完善关键代码修改分类需求分析代码实现分类管理业务开发 公共字段自动填充 问题分析 前面我们已经完成了…

A股上市公司MSCI指数和ESG评级效果(2010-2021年)

1、数据来源&#xff1a;摩根斯坦利资本国际公司(Morgan Stanley Capital International) 2、时间跨度&#xff1a;2010--2021 3、区域范围&#xff1a;A股上市公司 4、指标说明&#xff1a; ESG是英文Environmental&#xff08;环境&#xff09;、Social&#xff08;社会&…

《人月神话》(The Mythical Man-Month)6贯彻执行(Passing the Word)

《人月神话》&#xff08;The Mythical Man-Month&#xff09;Chapter 6. 贯彻执行 Passing the Word他只是坐在那里&#xff0c;嘴里说&#xff1a;"做这个&#xff01;做那个&#xff01;"当然&#xff0c;什么都不会发生&#xff0c;光说不做是没有用的。- 哈里杜…