章节索引 :

Java 线程内存模型

1. 前言

本节内容是从操作系统的层面谈并发,本节课程我们需要掌握如下内容:

  • 了解 Java 的内存模型定义,是 Java 并发编程基本原理的基础知识;
  • 从概念上了解线程的私有内存空间和主内存,能够从全局上了解线程是如何进行内存数据的存取操作的;
  • 了解线程拥有私有空间的意义,私有空间能够为线程提供独有的数据,其他线程不可干扰;
  • 在多线程环境下,主内存操作共享变量需要注意的事项需谨记,数据安全问题很重要;
  • Java 线程也是拥有生命周期的,了解它的生命周期,从宏观上了解线程。

2. 什么是 Java 的内存模型

定义: Java 内存模型(即 Java Memory Model,简称 JMM)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。

3. Java 线程的私有内存和主内存

首先看下图,图中展示了Java 的内存模型。

图片描述
工作内存(私有):由于JVM 运行程序的实体是线程,而每个线程创建时 JVM 都会为其创建一个工作内存(栈空间),用于存储线程私有的数据。线程私有的数据只能供自己使用,其他线程不能够访问到当前线程私有的内存空间,保证了不同的线程在处理自己的数据时,不受其他线程的影响。

主内存(共享):Java 内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问。从上图中可以看到,Java 的并发内存模型与操作系统的 CPU 运行方式极其相似,这就是 Java 的并发编程模型。通过创建多条线程,并发的进行操作,充分利用系统资源,达到高效的并发运算。

4. 线程拥有私有空间的意义

我们知道,线程的私有空间中存储的数据,仅供当前线程自己使用,其他线程不能够对数据进行访问。线程的私有空间会存放很多程序运行时所必须的数据,如:

程序计数器:记录当前方法执行到了哪里,以便 CPU 切换回来之后能够继续执行上次执行到的位置,而不会进行重复执行或者遗漏。

局部变量:局部变量是方法中的变量,只供当前方法使用。

方法参数:Java 方法会定义自己的入参,入参的真实值也会记录到内存空间供当前线程使用。

由于线程的内存空间会存放很多数据,这里只提以上三中数据以供同学理解线程私有空间的意义。
为了加深理解,我们一起看一个简单的代码示例并进行分析:

public class DemoTest{
    public static void main(String[] args) {
        sum(10); // 解析点 3
    }
    public static void  sum(int num) {
        int i = 5; // 解析点 1
        set(); //解析点 2
        System.out.println("num+i = "+ (num + i));
    }
    public static void  set() {
        int  i = 100;
    }
}

在给出结果之前,我们来分析下:
解析点 1 :设置 i 的值为 5;
解析点 2: 调用 set() 方法,逻辑如下。

public static void  set() {
        int  i = 100;
    }

那最终的结果是多少呢?
解析点 3:我们传入的 sum 的参数值是 10,如果想确定结果,只要确定另外一个加数 i 的值就行了。我们通过分析,在方法 sum(int num) 中的 int i = 5 与方法 set() 中的 int i = 100 是两个不同的方法的局部变量,属于线程私有的。互相不会影响,所以set() 方法中的 int i = 100 不会影响最终的结果:

num+i = 15

5. 主内存操作共享变量需要注意的事项

  • 确定是否是多线程环境:多线程环境下操作共享变量需要考虑线程的安全性;
  • 确定是否有增删改操作:多线程环境下,如果对共享数据有增加,删除或者修改的操作,需要谨慎。为了保证线程的同步性,必须对该共享数据进行加锁操作,保证多线程环境下,所有的线程能够获取到正确的数据。如生产者与消费者模型,售票模型。这些会在后续章节进行代码实战演练;
  • 多线程下的读操作:如果是只读操作,对共享数据不需要进行锁操作,因为数据本身未发生增删改操作,不会影响获取数据的准确性。

6. Java 线程的生命周期

每个事物都有其生命周期,也就是事物从出生开始到最终消亡这中间的整个过程。在其整个生命周期的历程中,会有不同阶段,每个阶段对应着一种状态,比如:人的一生会经历从婴幼儿、青少年、青壮年、中老年到最终死亡,离开这人世间,这是人一生的状态。

同样的,线程作为一种事物,也有生命周期,在其生命周期中也存在着不同的状态,不同的状态之间还会有互相转换。

Java 线程的声明周期会经历 6 中不同的状态变化,后续章节会有详细描述。从线程的创建到线程执行任务的完成,即 Java 线程的生命周期。

7. 小结

Java 并发理论基础是基于Java 的内存模型的,了解 Java 内存模型,能够更有助于后续对并发知识的理解和运用了。Java 的内存模型是并发原理的基础,在了解内存模型的基础上去理解共享内存和私有内存,了解不同内存状态以及 Java 线程的生命周期至关重要。