jvm加载以及执行过程
1.它会通过类装载子系统把我们的class文件加载到方法区(元空间)
2.元空间会根据的元数据对象,去堆中生成结构化的java.lang.class对象(如果是new或者反射操作,还会在堆中创建这个class对象的实列)
3.启动main函数后台创建一个线程栈,去执行我们加载到元空间的代码(一些方法的字节码...),并开辟出main函数的栈帧
4.当遇到 int a = 0; 时,字节码执行器知道要把int a = 0;,压入到栈帧顶部,但是实际压入栈帧的操作是有cpu来最终执行的
5.当字节码执行器执行int a = 0压入到栈帧顶部指令操作时,解释执行器会把这个指令翻译成汇编指令(硬件原语),此时再由硬件把硬件原语翻译成二进制(硬件具有硬编码的能力),但此时cpu并不一定会执行这个线程上的代码
6.因为java是内核线程模型,所以操作系统底层会维护一个线程变量池,线程变量池里存放的都是需要执行代码的线程,当cpu调度到那个压栈指令的线程时,才会执行那个线程上面的代码,执行对应的压入栈帧的指令
7.线程变量池里的线程,跟线程栈一一对应
volatile底层可见性保证
volatile底层依赖于 lock硬件原语
总线锁:在以前cpu技术不发达的时候多核cpu,并不能发挥多线程并发处理的能力,主要是因为总线锁其实是串行的,就好比小明打电话给你,那么小红就不能打给你了,这个线一直被小明给占用着,直到小明挂机了,小红才能打进来,那么线程也是一样
mesi协议:缓存一致性协议是这么工作的,在我们硬件里面约定好了一种协议,cpu在启动之后会采用一个监听模式一直会去监听总线里面消息的传递,也就是说有任何人从总线里面拿了些什么东西,只要你被lock前缀修饰了cpu都能感知得到
1.当我CPU A读取内存里面的一个数据x的时候,我要先通过总线去读取内存里的x变量,当发现读取的这个变量被lock前缀修饰了
那么这个x变量通过总线的时候就会被CPU B给监听到,当读过去之后CPU A就会产生一个x变量的副本,并给x变量的副本标记为e(独占)
因为此时这个x副本还只有CPU A持有
2.那么这个时候我CPU B也要读这个x数据,当CPU B读取这个x数据经过总线的时候,也会被CPU A给监听到,那么当CPU A监听到了后
就把e更改为了s状态,那么CPU B也会复制产生一个x的副本并标记为s
3.当我CPU A要修改数据x的时候,那么CPU A会对缓存行加锁,并向外部发送一个消息->本地写缓存行,
当CPU A先发送了本地写的信息,CPU A会把自己的x数据副本的状态变成m,而这个时候CPU B会监听到有人要本地写缓存了,把
自己的数据x副本状态更新为i
4.如果两个cpu同时加锁,并且同时发送本地消息,那么就由总线来判决由谁来修改,并通知到各个cpu
5.当读取的数据的大小超过缓存行的大小64字节,缓存一致性协议就会升级成总线锁