00:00
各位同学,我们继续接下来我们要谈谈CS的底层落地实现的原理,是由谁赋能给它,可以通过不加锁都能保证我们的数据一致性,那我们通过前面的学习已经晓得,所以CS啊,就是比较并交换,那说穿了也就是我们这儿所说的判断我们和旧的期望值是否相等,相等换成最新的修改值,不相等的话,要么放弃,要么重来一次,重来的这个过程就是我们所谓的自学,那么它是如何保障这样呢?是通过硬件级别的保证,那么说穿了就是一条CPU的原子指令,后续我们还会申请啊,涉及到汇编,那么我们这样呢,就是说CS的原子性实际上是CPU的原语级别来实现的,是通过硬件保证的,不会造成所谓的数据不一致啊,那对于我们常见常用的。Automic integer,那么它底层我们来看一下源码这个类,我们会发现这个类下面会有一个非常重要的一个类叫UN safe类。
01:03
没问题吧,哎,那么他就是我们支撑我们奥米in底层实现的根本原因,说穿了就是这哥们为什么你要底层赋能的,是有个UN safe类对他赋能它。通过这样的方式获得这个类,且自带着一个volatile级别的int value值。好,那么下面面试中呢,一定会被问到,你要是了解CS啊,用过原子操作类,那么谈谈你对UNS类的理解,来考察一下你聊没聊过这个底层的源代码?走起。首先我们来看看类它在哪。那么刚才我们也翻了翻查了一下源代码,它呢是用unafe.get Una这个类来获得,那么大家请看上面的注释也给你说明了,它呢就是通过S来赋能。那么。这个UN类是CS的这种落地思想的核心类,我们长期说过一句话,天上飞的理念必然要有落地的实现,理论是美好的,现实有时候是残酷的,那么你想把天上飞的理念落地的实现,就要需要有些核心类,那么CS这种思想最浓重的色彩一比给的就是C类,因为Java呢,一般呢是无法直接访问操作系统底层的。
02:21
他需要通过本地的native方法来访问,那么UNC类相当于一个后门,那么这个时候如果有C或者C加语言同语言的基础的同学都知道,一般啊在Java前删,我们是用C和C加加,是需要程序员自己去管内存的,这个时候呢,非常容易导致内存泄露,要是管不好的话,或者内存混乱,所以说Java了封装以后不让你去直接操作,但是由于Java底子还是他的娘价人就是C加加,所以他有后门,那么。这个类呢,是可以操作特定内存的数据,UN shift类,它凭什么可以获得呢?它一般是在我们的Java r t run点价包里面,这个包的子包下面,那么同学们我们来看一眼。
03:07
你自己本机的GDK装在哪无所谓啊,比如说我是这装在这在JAVA8这个版本下面的加Java运行环境力,这大家请看我们在下面的话呢,有个叫RT点价,然后我们用压缩软件。给它打开,此时按照我们的笔记,大家请看你这个UN类哪来的那么莫名其妙,在这儿呢,就有一个UN safe,大家请看S麦克那么。搁到这儿,Some。这个。来,同学们大家看这是不是有个按shift.class自节码文件啊,那么同理对应的,要是反编译以后,自然而然也会有一个在我们的open gdk8里面也会有一个UN safe.java稍后我们再去看这样的源码,那么从这儿告诉大家,这个东东哪来的?那么出娘胎就自带在我们的JDK环境里面的运行环境的RT架包下面,好,那么它内部呢?
04:04
可以的方法可以通过。类似于像C的指针一样直接操纵内存,哎,所以说它非常厉害,它是直接跟对内存负责的,因为Java中CS的操作执行依赖于CF类的方法,我们可以看到,再继续看一下源码。跑到我们的内。好,这儿没有下到源代码了啊,就没有了,这是最底层,除非你去看open gdk,那么所以说跟子就到这儿,那么大家请看在源码级别按shift.class里面啊,也就是我们这的这个UN shift.class大部分都是native方法,如果学过GVM的同学都晓得native方法站对吧?要调用底层操作系统第三方C源的函数库,所以说按S类中所有的方法几乎都是native体五修饰的,也就是说这个类中的方法都直接调用操作系统底层的资源来执行相应的任务,这是。第一步,第二个。大家请看unaly获得我们知道从哪来的了,它一一动手就是unsafe.object field ofset,那么value ofset也就这个当前对象,它的存放这个变量内存地址的偏移量也就是它的地址,那么它就是表示该变量的值在内存中的偏移地址,类似于一家公司所在地段的邮政编码或者门牌号码。那么按CFLY就会根据这个内存。
05:25
偏移地址来获取数据,来,同学们大家请看。我们最经典的啊,用过的。在这儿大家都晓得。Compare and set一点开以后,是不是就是compare and swap in this value of that,那么这个东东大家请看哪来的,就是由他来获得,所以说我们呢,就是说针对于当前对象,那么可以获得这个对象中该变量的值在内存中的偏移地址,有点类似于精确打击,那么就是这个对象的这个地址啊,那么看看,如果OK,按照我们的CS合适的话加个。
06:03
OK,好,那么get and相当于原子的A加加,那么这是我们的第二步了解了value of that,那么来,由于这个I加加,你变了以后,你是不是就要马上通知?主物理内存告诉他这个最新的值啊,那么怎么来?第一时间最快捷的去通知呢,别忘了我们这个值有V修饰,所以说第三步变量value用V修饰,保证了多线程之间对这个数据的内存可见性,那么你每一个多线程来调用这个方法来同学们,也就是我们这的。比较并交换。或者说是在。调用我们底层的啊,比如说在这个,那么我们的get and来同学们,那么相当于说这个就是我们的什么,你看by one the current value,那么只要某一个线程调用这个方法,相当于说最新的值已经加了一个。一最新值已经变更了,马上V立刻就获得我们的最新值,OK,好,所以说这个时候这个就是我们按C类最重要的三个关键点,那么我们也知道了I加加线程是不安全的,那么凭什么我们有奥m in加这么一个对象,用get and就能够。
07:20
被UN safe类赋能来保证,通过不加锁,通过cns自学这样的方式来保证我们的A加加达到原子性呢走。继续,那么cns呢,它呢是比较并交换,它呢是一条CPU的并发原语。那么它的功能是判断内存某个位置的值是否为预期,如果和预期值相符就更改为新值啊,否则什么都不做,或者是去自学再来一次啊。那么整个过程是原子的,通过我们的源码分解,我们都晓得这个类主要是利用CS加V和native的方法来保证原子操作,从而避免SNCH的开销。
08:02
执行效率大为提升,那这个就达到了不加锁还能保证原子性,性能还好,那么。它的改进是杠杠的来。这个。由于这些呢,我们都抓过图了,都是一回事啊,我们呢就不再反复切换out in.get get and过来,其实而言到这个方法。这个方法的话,调用的是Una类,那么Una类下面大家再请看,它又有这么一个方法,中间是一段do well的循环,为什么要是读爱啊,别忘了我们这儿是不是画过,想不到的话是不是自学啊,直到你成功为止啊,来我们解读一下源码,那么同学们请看。当前这个对象的这个内存地址里面是否要加个一,那么过来这儿如果我们用的是get and ADD in,就是一步步缩下来啊,其实呢,对外是叫increase,底层的是叫at int,都一样,首先先获得当前的最新值,然后来尝试一下是否正确,那么来假设当前。
09:10
你看。Get valtile最新的这个对吧?那么V1V二传过来,V1就是这个ZV2就是这个value of set内存地址偏移量,得到当前对象在这个地址上的这个值VALUE5,然后我们来进行一下我们的作业操作,比较并交换。如果VALUE1。就是这个当前对象,VALUE2就是这个value of set value5就是当前这个共享内存的。最新值。假设和我们的期望值一样,就是我们得到的这个值,进行了我们一定的操作以后,回来发现预期值还是一样的,那么就会把我们的当前的这个值啊,怎么着加个一,这个一就是这个参数Y64 OK,那么整体如果说蓝色框框这一块。
10:02
操纵成功返回的就是处处取反了以后,那么自然而然会跳出这个读L循环,那么说白了什么返回,我们刚才拿到这个VALUE5这个值啊。Get完成,但是就像是还有一个连消带打的操作,那么第二步它又把我们的值又更新了,OK,反之大家请看啊,我们先得到VALUE5啊。比较并交换compare and swap当前这个对象,这个对象里面的这个value of set内存地址值和我们的获得的VALUE5来相比。如果不一样了,那么对不起,整个蓝色框框返回的是false false取反以后说明本次提交失败,我们只好马上像这样一个来一次自选,OK,再回到这个读外循环体里面,再获得这个最新值,来看当前对象的这个地址,看看我们取得的这个值和我们的旧有的期望值是不是一样,如果一样了才能加个一不一样,那么对不起,不一样蓝色框框就是负四,再去反就是错,那么只好导致我们再来一次自学,整个过程就是这样。所以说。
11:15
最终返回的就是这个get拿到的值,然后往里面又添加了一次,它是分两步,所以说CS的并发允许体现在Java语原装就是这个包下面这个UN save.class里面的方法,调用UN save中的CS方法,Java虚拟机会把我们实现S的汇编指令,这是一种完全依赖于什么硬件的功能,哎,所以说它通过它呢就实现了原子操作,保障了数据的一致性,还不用通过加锁。那么再次强调,由于CS是一种系统级别的原语,原语属于操作系统级别的用语范畴。它是由若干条指令组成,用于完成某个功能的一个过程,那么扎是由底层原与级别保证,那么这种的话执行必须是连续的,要么一起成功,要么一起失败,那么所以说在执行过程当中是不允许被中断,也就是说CS是一条CP的原子指令,不会造成数据不一致,那么请大家呢,放心大胆的用,那么这个方法底层的业务逻辑就来自于这样的源码分析。
我来说两句