JVM如何执行synchronized修饰的方法

深入解析:Javasynchronized在JVM中的执行机制

首先市面上关于synchronized的资料已经很多了,但是大家对于底层的原理实现可能没有什么概念,大多都是死记硬背,所以我想通过实战的方式给大家带来一些不一样的体验。具体体现在哪些方面呢?

  1. 更系统。市面上目前虽然资料众多,但都是零散的。有些资料讲得东西甚至是相互冲突的,都不知道信谁的。我准备从Java层面到JVM层面到操作系统层面系统的去分析用synchronized后呈现的每个现象背后的本质。synchronized很多知识点市面上是没有资料讲的,我给它补上。

  2. 更接近真相。市面上的很多资料,有的是基于字节码解释器那块的代码yy出来的,有的是东拼西凑整合出来的,各个说的都像真的一样,把看的人搞蒙圈了。我准备从模板解释器代码入手,单步调试着研究,有些不确定的自己写代码去证明,争取分享给大家的都是本来如此的知识。不确定的地方我会标注出来。

  1. 授人以鱼不如授人以渔。我会以大家学完后能够手写出synchronized的标准来设计这个专栏。因为从我自己研究的角度来说,抛开语言的障碍,synchronized的每种机制如果让你实现你手足无措,那你还是没有真正地理解synchronized。言外之意就是你不一定要去手写,但是你在脑海中回想,比如CAS、锁膨胀、锁对象加锁解锁……你大概知道代码是怎么写的。

本篇文章是第一篇,聚焦分析JVM是如何执行synchronized修饰的方法的:

  1. 编译系统是如何处理synchronized关键字的

  2. JVM是如何选出锁定的对象的

  3. 模板解释器为了提升效率做了什么

  4. 什么情况会由执行例程切入C++代码

  5. 如何单步调试synchronized

01  方法入口点

JVM执行Java方法都需要先构建运行环境,再去执行字节码指令。

这个运行环境包括:创建栈帧、从调用者堆栈拷贝参数、给this指针赋值…如果是synchronized修饰的方法,还需要:根据是否是静态方法来计算出锁对象,即是当前对象实例还是Class对象、进行上锁…

图片

因为调用每个方法都需要构建运行环境,都需要做这些事情,所以JVM把这套流程打包,封装成一个一个的执行流程。在JVM术语中,一般字节码指令的处理逻辑称为执行例程,这里为了做区分,起了个新的名字,叫entry point,翻译过来就是入口点。

JVM中有很多entry point,都存储在entry table中。与方法调用相关的比较常见的是这四个。非native方法一般对应的就是前两个入口点,被synchronized修饰对应的是zerolocals_synchronized,否则是zerolocals。

图片

这些entry point是什么时候生成的呢?JVM启动的时候,看代码

图片

那Java中的每个方法,何时与这些入口点进行绑定的呢?在链接阶段。看代码

图片

好像没看到synchronized修饰的方法的入口点是如何绑定的对吧。这个得追entry_for_method代码逻辑

图片

02   如何执行

JVM是如何执行synchronized修饰的方法的呢?这个得从编译阶段、链接阶段、运行阶段三个阶段来分析。任何语言的任何语法糖都是由编译系统与运行系统配合完成的。这三个阶段中,编译阶段是编译系统做的事情,链接阶段与运行阶段是运行系统做的事情。接下来展开来说下。

编译系统

JVM在运行时是如何知道我现在要执行的方法有没有被synchronized修饰呢?是通过方法的访问权限为来识别的。

图片

这个数据是在编译阶段生成的,通过IDEA插件jclasslib可查看。

运行系统

如果是synchronized代码块,那在链接阶段会把这个方法当成普通的方法来处理,绑定的执行流就是zerolocals,最终处理synchronized逻辑是在执行monitorenter指令时。

如果是synchronized修饰方法,在链接阶段绑定执行流zerolocals_synchronized。这两者的区别是什么呢?其实生成的执行流是同一套代码,区别就是有个判断,如果是synchronized修饰的方法,会执行lock_method。看代码。

图片

lock_method方法的逻辑是汇编风格写出来的,不太好理解,我就用伪代码解释下

图片

至此,JVM是如何执行synchronized修饰的方法的逻辑就讲完了。当然,synchronized还有很多很多内容,我会逐步分享给大家。感兴趣的小伙伴可以关注一波。我的公众号:硬核子牙。

最后

我知道小伙伴们心理在想什么:我什么时候也能像子牙老师这样,哪里不懂就直接深入源码去寻找答案、工作腻了,我也想写一些自己感兴趣的算法……

实话实话,底层这玩意学起来确实难。有时候遇到问老师就是红手题都找不到答案,甚至有时候百度都不知道怎么查…自己瞎学瞎报班,浪费钱浪费时间,还没有结果。操作系统内核我也是报班学的,自己学了一段时间,真心学不会。我的老师就是红手指的首席科学家唐老师,前瑞星杀毒的技术专家,虚拟化领域的TOP级人物。

给大家看看我之前写的一些项目,证明下我不是在吹牛。如果对手写JVM和linux实战课程感兴趣的话欢迎来咨询学习

图片

图片

<think>好的,我现在需要详细分析用户的问题,关于Javasynchronized修饰方法修饰代码块的区别。用户之前已经询问过Android自定义圆角View的实现,但这次的问题转向了Java多线程的synchronized关键字,看起来用户可能是在学习Android开发过程中遇到了多线程同步的问题,或者是扩展学习Java并发编程。 首先,我需要回顾用户提供的引用内容,确保准确理解各个引用的要点。用户引用了五篇资料,主要涉及synchronized的不同使用方式、实现机制、锁的范围以及性能影响。根据这些引用,尤其是引用[5]明确指出同步方法和同步代码块的主要区别在于锁的范围和性能影响。同步方法锁的范围更大,可能影响性能,而代码块可以更精确控制锁的范围,提升性能。 接下来,我需要结合这些引用,系统地整理出两者的区别。可能需要从以下几个方面展开: 1. **锁的范围**:同步方法默认锁定整个方法,而代码块可以指定锁定对象和范围。 2. **锁的对象**:同步方法如果是实例方法,锁的是当前实例(this);静态方法锁的是类对象。代码块可以灵活选择锁对象。 3. **粒度控制**:代码块允许更细粒度的同步,减少锁的持有时间,提升性能。 4. **字节码实现**:引用[2]提到,synchronized方法和代码块在字节码层面的实现不同,方法使用ACC_SYNCHRONIZED标志,代码块使用monitorenter和monitorexit指令。 5. **灵活性**:代码块可以选择不同的锁对象,实现更复杂的同步策略,比如锁分离。 6. **可维护性**:方法级的同步可能更简洁,但代码块提供更明确的同步区域。 同时,需要考虑用户可能的疑问点,例如:为什么锁的范围影响性能?如何选择锁对象?是否有实际例子说明两者的使用场景差异? 需要确保回答结构清晰,分点列出区别,并且结合引用中的例子,比如引用[4]中的Phone类示例,说明代码块同步和同步方法的不同效果。此外,引用[5]提到的性能影响需要强调,避免用户错误地认为同步方法总是性能更差,而应指出在适当的情况下,合理缩小同步范围能提升性能。 另外,用户之前的问题关于Android自定义View,虽然当前问题转向Java多线程,但可能用户是在开发Android应用时遇到了需要同步的情况,比如在自定义View中处理多线程更新UI的问题。不过当前问题并不涉及Android特定部分,所以应专注于Java层面的解释。 最后,需要验证回答是否符合用户提供的引用内容,避免引入未提及的信息,同时确保引用标注正确,例如引用[5]关于性能的部分,引用[4]的示例等。同时,检查是否有遗漏的关键点,比如可重入性、异常处理等,但根据提供的引用,这些点未被提及,可能不需要深入讨论。 总结思考过程后,组织回答结构,分点阐述区别,并附上示例代码和实际应用建议,确保用户能够清晰理解两者的区别及适用场景。</think>### Javasynchronized方法与代码块的区别 #### 1. **锁的范围与粒度** - **synchronized方法**: 直接修饰整个方法,锁的范围是**整个方法体**。若为实例方法,锁定的是**当前对象实例(this)**;若为静态方法,锁定的是**类的Class对象**。 ```java public synchronized void method() { // 整个方法体被锁定 } ``` - **synchronized代码块**: 仅锁定代码块内逻辑,可以**灵活指定锁对象**(如`this`、其他对象或类对象),锁的范围更小,**粒度更细**。 ```java public void method() { // 非同步代码 synchronized (lockObject) { // 仅锁定此代码块 } } ``` #### 2. **字节码实现机制** - **方法级同步**: 通过`ACC_SYNCHRONIZED`标志实现,JVM方法调用时自动获取锁,方法结束时释放锁[^2]。 - **代码块同步**: 通过`monitorenter`和`monitorexit`指令显式控制锁的获取和释放[^2]。 #### 3. **性能影响** - **同步方法**: 锁的范围较大,可能导致**线程竞争时间增加**,降低性能[^5]。例如: ```java public synchronized void process() { // 耗时操作(整个方法被锁定) } ``` - **同步代码块**: 仅锁定关键代码段,**减少锁持有时间**,提升并发性能[^5]。例如: ```java public void process() { // 非关键操作(无需同步) synchronized (this) { // 关键共享资源操作 } } ``` #### 4. **锁对象的灵活性** - **同步方法**: 锁对象固定(实例方法锁`this`,静态方法锁`Class`对象),无法更改。 - **代码块同步**: 可自由选择锁对象,实现**细粒度的锁控制**。例如,使用独立锁对象避免锁冲突: ```java private final Object lock1 = new Object(); private final Object lock2 = new Object(); public void methodA() { synchronized (lock1) { /* ... */ } } public void methodB() { synchronized (lock2) { /* ... */ } } ``` #### 5. **代码可读性与维护性** - **同步方法**: 代码更简洁,但可能隐藏同步逻辑,导致后续维护困难。 - **同步代码块**: 明确标注同步区域,便于理解代码的线程安全设计。 --- ### 实际场景示例 引用[4]中的`Phone`类展示了代码块同步的典型用法: ```java class Phone { public void sendSMS() { synchronized (this) { // 仅锁定发送短信的逻辑 System.out.println(Thread.currentThread().getName() + "=>发送短信..."); } // 此处代码不受锁保护,可能被其他线程抢占执行 System.out.println("锁外的其他操作..."); } } ``` 若改为同步方法,则整个方法体被锁定: ```java public synchronized void sendSMS() { System.out.println(Thread.currentThread().getName() + "=>发送短信..."); System.out.println("锁外的其他操作..."); // 此部分也被锁定 } ``` --- ### 总结对比表 | **特性** | **synchronized方法** | **synchronized代码块** | |------------------|---------------------------------------|---------------------------------------| | 锁范围 | 整个方法 | 仅代码块内部 | | 锁对象 | `this`(实例方法)或`Class`(静态方法)| 可自定义(任意对象) | | 性能 | 可能较差(锁范围大) | 更优(锁范围可控) | | 灵活性 | 低 | 高(支持锁分离等策略) | | 字节码实现 | `ACC_SYNCHRONIZED`标志 | `monitorenter`/`monitorexit`指令[^2] | --- #### 选择建议 - 优先使用**synchronized代码块**,仅在简单场景或需要明确方法级同步时使用同步方法。 - 对性能敏感的场景,尽量缩小同步范围,避免锁竞争[^5]。 - 复杂同步逻辑(如分段锁、读写锁)必须通过代码块实现。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值