#Java#互联网大厂面试必问的Java内存模型以及并发编程( 二 )


缓存一致性既然每个核中都有单独的缓存 , 那我的 4 核 8 线程 CPU 处理主内存数据的时候 , 不就会出现数据不一致问题了吗?
为了解决这个问题 , 先后有过两种方法:总线锁机制和缓存锁机制 。
总线锁就是使用 CPU 提供的一个LOCK#信号 , 当一个处理器在总线上输出此信号 , 其他处理器的请求将被阻塞 , 那么该处理器就可以独占共享锁 。 这样就保证了数据一致性 。
但是总线锁开销太大 , 我们需要控制锁的粒度 , 所以又有了缓存锁 , 核心就是“缓存一致性协议” , 不同的 CPU 硬件厂商实现方式稍有不同 , 有MSI、MESI、MOSI等 。
代码乱序执行优化为了使得处理器内部的运算单元尽量被充分利用 , 提高运算效率 , 处理器可能会对输入的代码进行「乱序执行」(Out-Of-Order Execution) , 处理器会在计算之后将乱序执行的结果重组 , 乱序优化可以保证在单线程下该执行结果与顺序执行的结果是一致的 , 但不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致 。
乱序执行技术是处理器为提高运算速度而做出违背代码原有顺序的优化 。 在单核时代 , 处理器保证做出的优化不会导致执行结果远离预期目标 , 但在多核环境下却并非如此 。
多核环境下 ,如果存在一个核的计算任务依赖另一个核的计算任务的中间结果 , 而且对相关数据读写没做任何防护措施 , 那么其顺序性并不能靠代码的先后顺序来保证 , 处理器最终得出的结果和我们逻辑得到的结果可能会大不相同 。
编译器指令重排除了上述由处理器和缓存引起的乱序之外 , 现代编译器同样提供了乱序优化 。 之所以出现编译器乱序优化其根本原因在于处理器每次只能分析一小块指令 , 但编译器却能在很大范围内进行代码分析 , 从而做出更优的策略 , 充分利用处理器的乱序执行功能 。
内存屏障尽管我们看到乱序执行初始目的是为了提高效率 , 但是它看来其好像在这多核时代不尽人意 , 其中的某些”自作聪明”的优化导致多线程程序产生各种各样的意外 。 因此有必要存在一种机制来消除乱序执行带来的坏影响 , 也就是说应该允许程序员显式的告诉处理器对某些地方禁止乱序执行 。 这种机制就是所谓内存屏障 。 不同架构的处理器在其指令集中提供了不同的指令来发起内存屏障 , 对应在编程语言当中就是提供特殊的关键字来调用处理器相关的指令 , JMM里我们再探讨 。
Java内存模型Java 内存模型即 Java Memory Model , 简称 JMM 。
这里的内存模型可不是 JVM 里的运行时数据区 。
「内存模型」可以理解为在特定操作协议下 , 对特定的内存或高速缓存进行读写访问的过程抽象 。
不同架构的物理计算机可以有不一样的内存模型 , Java虚拟机也有自己的内存模型 。
Java虚拟机规范中试图定义一种「 Java 内存模型」来屏蔽掉各种硬件和操作系统的内存访问差异 , 以实现让 Java 程序在各种平台下都能达到一致的内存访问效果 , 不必因为不同平台上的物理机的内存模型的差异 , 对各平台定制化开发程序 。
Java 内存模型的主要目标是定义程序中各个变量的访问规则 , 即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节 。 这里的变量与我们写 Java 代码中的变量不同 , 它包括了实例字段、静态字段和构成数组对象的元素 , 但不包括局部变量和方法参数 , 因为他们是线程私有的 , 不会被共享 。
JMM 组成