Java内存模型

Java 内存模型内部原理

JVM 中的 Java 内存模型将内存分为线程栈和堆。

JVM 中的每一个线程都有自己的线程栈。线程栈包含方法的信息,也叫调用栈。

线程栈也包含调用栈中的方法的局部变量。线程创建的局部变量对其他县城来说是不可见的。

类型是基本类型的局部变量(boolean,byte,short,char,int,long,float,double)都存放在线程栈中,对其他线程不可见。线程可以传递这些基本类型变量的拷贝到其他线程中,但是不能共享这些基本类型变量本身。

应用创建的所有对象都存放在堆中,没有区分是哪个线程创建的对象。也包含基本类型的对象版本(比如 Byte,Integer,Long 等等)。不管是局部变量还是成员变量,对象都是存放在堆中。

硬件内存架构

硬件内存架构和 Java 内存模型有些不同。

CPU 寄存器 -- CPU 缓存 -- 内存

Java 内存模型和硬件内存模型架构的桥接

虽然 Java 内存模型和硬件内存模型是不同的,但是硬件内存模型不会去区分线程栈和堆。在硬件中,线程栈和堆都存放在内存中。部分线程栈和堆有可能出现在 CPU 缓存和 CPU 寄存器中。

因为对象和变量可以存放在各种内存区域中,就会出现一些问题,主要有两个

  • 线程写入共享变量的可见性
  • 读、检查和更新共享变量时的竞态

共享对象的可见性

count 初始值为 0。CPU A 的线程 A 和从内存读取变量 count 到 CPU 缓存,A 把 count 加 1,但是没有把结构写回内存。这时候,CPU B 的线程 B 从内存读取变量到 CPU 缓存,这时候 count 的值为 1,线程 B 不知道 count 的值已经变化了,正确的值应该是 2。

可以使用 Java's volatile keyword,它可以保证变量是直接从内存读取,并且更新变量之后会写回内存中。

竞态

和上面的例子一样,经过线程 A 和线程 B 对 count 的加 1,count 的值应该是 2。但是在线程对 count 进行写入之后,没有把 count 刷新到内存中去,count 的值只加 1。结果是不对的。

这种情况可以使用 Java synchronized block,同步块可以保证任何时候只有一个线程可以进入临界区。同步块也可以保证所有的同步块中的变量都会从内存读取,同时当线程离开同步块之后,所有被更新的变量都会写回内存中,不管这个变量是否声明为 valotile 的。

原文地址:https://www.cnblogs.com/okadanana/p/5911227.html