天天看点

Java内存模型与volatile关键字不为人知的关系

1 博文主旨

行文主要围绕以下3个问题展开阐述:

  1. volatile关键字作用有哪些?
  2. volatile关键字有哪些运用场景?
  3. Java内存模型与volatile之间究竟有何关联?

2 Java内存模型 VS JVM运行时数据区

2.1 Java内存模型

Java虚拟机支持多线程任务的执行。在多线程运行场景下,若线程间未能保证同步的正确性,线程的行为可能会出现混淆和违反直觉。

在这种情况下,我们也许迫切需要一些保障措施:比如,当多个线程修改了共享内存中变量的值时,后续线程应该以哪一次修改的值作为基准进行读取?类似的语义(或者称规则)在Java语言中主要用于描述多线程程序的合法行为,又由于此类型的规范区别于硬件体系结构的内存模型,因此这些语义被称为Java编程语言内存模型,简称Java内存模型。此为Java内存模型的由来!

鉴于还是有许多人跟博主一样,一开始愣是将Java内存模型和JVM运行时数据区混为一谈,所以有必要对JVM运行时数据区作个简要说明。

2.2 JVM运行时数据区

JVM运行时数据区,按照线程是否共享,可划分为以下2个部分:

2.2.1 线程共享

所有线程都能访问这块内存区域的数据。包括:堆内存 和 方法区

其中,

堆内存:存储new出来的对象实例。

方法区:存储加载的类信息,常量、静态变量、编译后的代码等。

2.2.2 线程独享

每个线程都有其独立的专属内存空间。包括:虚拟机栈、本地方法栈和程序计数器。

其中,

虚拟机栈:每个线程在这块区域都有一块私有的内存空间。是为JVM执行Java方法(非native方法)而准备的。

本地方法栈:和虚拟机栈类似,不过它是为JVM执行native本地方法而准备的。

程序计数器:记录当前线程执行字节码的位置。其内存储的是字节码指令地址,如果执行的是native方法,则计数器值为空。每个线程在 程序计算器 中都拥有一块专属空间。CPU在同一时间,只会执行线程中的一条指令。

3 volatile关键字闪亮登场

3.1 volatile关键字解决了啥问题?
  • 可见性问题:在多线程语义下,一个线程对共享变量的修改,其他线程能够及时感知到且保证读取到最新的修改值。

3.1.1可见性问题是怎样得到保障的呢?

Java内存模型规定:对volatile变量v的写入,与所有其他线程后续对v的读操作同步。(这里的 ***同步*** 等同于 ***可见***)
           
3.2 volatile关键字有哪些功能?
  • 禁止缓存(对volatile变量的读写都不涉及CPU高速缓存)。
  • 对volatile变量相关的指令不做重排序。

4 小结

  • Java内存模型是多线程程序中对线程合法行为的语义规范。
  • Java源代码经编译后得到.class字节码文件,字节码将交由JVM去处理执行。这其中涉及到JVM运行时数据区。JVM运行时数据区主要分为:堆内存、方法区、虚拟机栈、本地方法栈和程序计数器。其中堆内存和方法区属于线程共享的内存区域。虚拟机栈、本地方法栈和程序计数器则属于线程独享的内存区域。
  • volatile关键字主要解决共享变量的可见性问题。对于volatile变量,禁止缓存和不使用指令重排序。

也就是说,volatile关键字发挥的作用,是在Java内存模型所允许的多线程程序语义之内的。