00:00
同学们,我们继续接下来继续对UNC类进行源码和底层汇编的相关的介绍和讲解,这一块很重要,请跟着来。那么下面我们继续,当我们拗了一个原子整形类的get and increase,类似于原子板的加加的时候,那么底层怎么来的来,先是它,然后调用的是UN get and ADD in。但是它然后完了以后,同学们请看,干到了UN safe.class那么这个过程大家请看int value5和我们其实我们源码和抓,和我们老图笔记的抓图是一回事,那么来吧,也就是先get嘛,对吧,先获得,然后我们去做我们的业务,去修改,回来以后,如果说旧的期望值和我们之前那个是一样的,那么来更新,否则的话自学好,那再往下。点这个方法,那么基本上就是到我们的了,那么我们来往下继续来看一下这个native底层更深的一步是怎么来的,来,同学们。
01:04
挨个挨个的翻查了,先是这个,然后再到这个,然后再到这个,那么最终。到这儿了以后,同学们都晓得在306行这我就停这了,那么它底层就要用我们的open gdk的按shift.java来进行查看,所以呢,在这块在open jdk源码里面查看,按save.java那这一段在我们idea里面的Java源码底子就长这个样。OK,对应下来,那么就是object。Offset和我们的偏移的修改值来,它的原理是这样的。假设AB2个线程都同时。执行我们的get and ADD int方法分别跑在不同的CPU上面啊,甚至是就代表是多核,那么首先假设初始值这个原子整形内里面值是三,那么也就是说主物理内存里面这个对象的值就是三,相当于说啊,我们最经典就是在这儿,比如说我们这是五嘛,相当于说我们这六了,这么一个实体对象,它的初始值就是三,OK,好,那么过来。
02:12
根据Java内存模型线程,AB各自持有一份值为三的value的副本,分别到各自的工作内存,那么早上我们这也说过啊,比如说这是一个三,那么相当于说AB甚至是C全都是三,各自拷走一份,在自己的工作私有的工作内存里面各自忙活。来了,这是第一步,第二步。形成A,通过get value这个方法,那么。Y61Y62相当于这个对象这个内存地址啊,拿到了,我的值是三,假设啊,因为多线程切换嘛,此时A就被挂起来了啊,因为有一个CPU的轮巡的抢占时间,那么A停止,那么此时A的工作并没有做完,并没有写回去,那么B。捷足先灯也通过这个得到三,此时刚好线程B没有被挂起,并且一路绿灯往前直行,也执行了我们的比较并交换的这个方法,那么下面大家请看,那么现在有点类似于,就是说虽然有AB2个线程,但是A被挂起了,只有B1个人唱独角戏,那么所以说B。
03:21
读到的这个值是三,然后呢,他就去干他各自的工作,干完以后准备写回去,回来的时候还是这个对象,还是这个D值,我希望这个值还是三,由于A已经被挂起,没有人跟我抢,所以说非常的顺利,那么将会导致B写回去调用这个原来值是三加S成功这一段这个值修改为四。这个方法返回true true取法,那么就是得到了我们的一个什么first first跳出多循环,那么相当于说。此时这个return就是我们的那个三,然后线程B又把里面的最新值啊改为了四啊,线程B打完收工,一切OK,那么B完了。
04:08
此时线程A恢复啊,执行同样的这个方法,突然发现手里的这个值啊,那么现在我得到这个三,好,我这个A苏醒了,回去以后还是这个对象,还是这个地值值,我期望旧有的期望值还是三,但抱歉,现在这个值已经变成了四,那么所以说A线程这段整体就是一个什么false false又取反是错,A线程只好重新回去进行自学,再来一次,OK,所以说呢,我们呢,回到了我们的第四步。A发现自己手里的数值三和主内存的值数值四是不一样,因为四已经由B提前改好了,说明该值已经被其他线程,也就是这个B抢先一步修改过了,那么线程A本次修改失败,只能重新读取,重新再来一遍,在四的基础上再去做自己的工作。那么最终。
05:01
我们大家看,那么线程A重新获得这个value值,三被放弃作废,拿到这个四,因为四啊是被value修饰的,所以其他线程对象修改线程A也能看到,直到现在最新的值已经从三变成四,所以说线程A继续执行这个方法,进行比较替换,直到成功为止,OK,好,那么这个就是我们。整个类它是什么,以及它结合我们底层的自学这种思想来进行相关的配合,保证不加锁,却能够保证我们得到原子性的效果来。那么继续。再来回到我们的源码,我们都晓得都是native,那么这个native是调底层第三方C和操作系统的第三方函数,那么它修饰方法代表是底层方法,那么下面我们来看一下。如果你是非算专业的,我不要求你懂,你可以不听,因为这块需要一点点汇编知识啊,但是呢,老师呢,都给大家呢,整理好了我们对应的回答的话术,那么请同学们也是耐着性子过一下,那么到了我们的Una类当中的这个方法再往下翻,那么是一个本地方法,也是那个native方法长什么样?那么这个方法是C里面的un.CPP。
06:17
这个C加加里面的程序,那么老师把它拷出来,我们大家请看啊,其实也不难,那么各种引用和指针数字传过来,我们来首先来这儿啊,就是这一行,先想办法拿到value在内存中的地址啊,然后根据偏移量。然后呢,计算出这个value的值,完了以后,它的底层是原子类的compare,类似于change,也是比较并交换,那么首先这个函数是来进行比较和交换的。X啊,是要交换的值啊,这个呢,当然就是我们的那个。地址,而E是要比较的值,那么如果这个KCS成功了,返回的期望值是E,那么此方法返回错,OK,如果说失败了,返回内存中的value值不等于E,此方法返回false,那么这段代码就是来保证了比较并交换的一个操作。也就是我们这儿假设你在idea里面看到的是这么一行代码,底子再往下翻,那么就是按safe类点CP。
07:22
这个方法更深的及底子全都是一堆汇编,但是呢,你只需要记着红色这一行,那么也就是说由我们的JDK所提供的CS机制,在汇编层级会禁止变量两侧的什么指令优化,然后利用这个。比较并更新原子池,那么来获得了我们的更新和保证。哎,它的底层。汇编级别的代码,类似于这样一些C的原理,好,那么下面我们来看看这个方法啊,前面也说过了,比较并交换,它呢,相当于我们这儿的这个compare and swap in OK,那么它底子又是什么呢?走起。
08:04
来。那么现在的话呢,它呢,这个方法里面又牵扯到什么?系统平台的调度,因为它要保证这种原语级别,就是你不同的CPU,不同的操作系统都要有效,所以说在这块它会根据操作系统的类型调用不同平台下的重载函数,在这个预编译期间,编译器会决定调用哪个平台下的重载函数,比方说你是呃,Linux啊,或者是Mac或者是Windows,好,那么在这块我们去相应的选择这些地址,那么不同的操作下面会调用不同的这个函数重载,那么。老师本次用的是WINDOWS10这个系统,所以说呢,在这块,那么同学们走起,那么来判断is more process是不是操作系统,是不是多核的,那么来在这块,这个就是汇编三个目指令啊,大家了解一下就行,不要求大家掌握,那么可以看一下。那么现在这三个主要就是什么,你看底层是不是CPU英语级别,为什么呢,保证说它不会冲突底子,其实上而言还是会对什么加一个总线的,这把锁只能有一个线程进去改来这个单词比较。
09:15
并交换,那么在这块有点是C的什么double,无二的一共是什么两个字四字节,这个PTR呢是pointer,类似于指针,OK,好了,那么指过去了以后,那么就是说将我们前面定义的这个嗯,EAX这个呢是比较器用的值与EDX。他们这个值呢进行对比相同,那么就会将这个。我们这前面定义的ECX的就是ex change这个value中的值存到我们的目的地EDX的内存单元之中,发生一次变更和操作,OK,所以说呢,你不用担心,底子是操作系统和CPU原语级别会给你加锁,所以说我们在代码层面就不用写我们的S了,OK,那么最终我们可以得到一个总结。
10:06
我们如果上面那堆汇编对于非计算机专业同学,你不用逐行看懂,那么你只是了解底子是怎么给你加锁的,我们从源码级别给大家说清楚,那么最终你面试回答你只需要CS是靠硬件实现,就是我们刚才所说的CPU原语级别在。硬件层面提升效率,最底层还是交给我们的CPU。这种硬件来保证原子性和可见实现方式是基于硬件平台的汇编指令,那么对于我们的CPU,如果是X86指定体上都是用这个汇编指令,Change X compare change X change这样的一个指令,核心思想也就是刚,也就是我们的这张图。好吧,说了很多遍了,我再赘述,比较要更新变量的值和预期这样的来。比较相等就更新,如果不相等,要么你本次操作结束自己放弃,要么自学再来一次,OK,好,那么这个就是我们底层汇编相关的源码分析。
我来说两句