jdk-synchronized源码学习
创始人
2024-02-19 07:07:03

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.classLast modified 2022-11-20; size 512 bytesMD5 checksum c1a83fca01614297b7553ecf31e0b979Compiled from "SynchronizedLock.java"
public class com.lock.SynchronizedLockminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref #7.#16 // java/lang/Object."":()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 #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 // "":()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: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."":()V4: returnLineNumberTable:line 8: 0public synchronized void method();descriptor: ()V//注意:ACC_SYNCHRONIZED 是用来标识是同步方法flags: ACC_PUBLIC, ACC_SYNCHRONIZEDCode:stack=2, locals=1, args_size=10: 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;)V8: returnLineNumberTable:line 10: 0line 11: 8public void method2();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=1, args_size=10: 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;)V8: returnLineNumberTable:line 14: 0line 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

相关内容

热门资讯

北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...