面试官:什么是Java内存模型?("深入解析Java内存模型:面试官常问知识点详解")

原创
ithorizon 6个月前 (10-20) 阅读数 23 #后端开发

深入解析Java内存模型:面试官常问知识点详解

一、Java内存模型的概述

Java内存模型(Java Memory Model,JMM)是Java虚拟机(JVM)的一个核心概念,它定义了Java程序中各种变量(线程共享的变量)的访问规则,同时也涉及到JVM的运行时数据区。JMM决定了一个变量的写入何时对另一个线程可见,它是懂得并发编程和线程稳固的基础。

二、Java内存模型的组成

Java内存模型核心由以下几个部分组成:

  • 主内存(Main Memory)
  • 工作内存(Working Memory)
  • 内存操作(Read、Use、Assign、Write)
  • 内存屏障(Memory Barrier)
  • 原子性(Atomicity)
  • 可见性(Visibility)
  • 有序性(Ordering)

三、主内存与工作内存

主内存是所有线程共享的内存区域,它存储了Java程序中的实例字段、静态字段和构成数组的元素。工作内存是每个线程私有的内存缓冲区,用于存储线程使用的变量的副本。

当线程对变量进行操作时,它会先从主内存中读取变量值到工作内存,然后在工作内存中对变量进行操作,操作完成后将新值写回主内存。

四、内存操作

Java内存模型定义了以下几种内存操作:

  • Read:从主内存读取数据到工作内存。
  • Use:在工作内存中使用变量。
  • Assign:将工作内存中的数据赋值给变量。
  • Write:将工作内存中的数据写回主内存。

五、内存屏障

内存屏障是JMM用来保证特定内存操作的执行顺序,它是一种同步机制。JMM通过插入特定类型的内存屏障来禁止特定类型的处理器重排序,确保内存操作的顺序性。

六、原子性

原子性指的是一个操作在其他线程看来是不可分割的,即线程在执行这些操作时不会被中断。JMM通过happens-before原则来保证原子性。以下是一些具有原子性的操作:

  • 基本读取和写入操作(如对基本数据类型的读取和写入)。
  • 使用volatile变量的读/写操作。
  • 锁的获取和释放。

七、可见性

可见性指的是当一个线程修改了共享变量的值后,其他线程能够立即得知这个修改。JMM通过volatile和synchronized两个关键字来保证可见性。

使用volatile关键字修饰的变量,当一个线程写入该变量时,它会立即被更新到主内存中,同时其他线程读取该变量时,会从主内存中读取最新值。

使用synchronized关键字同步代码块或方法时,当一个线程进入同步代码块或方法前,它会清空工作内存中的共享变量值,当退出同步代码块或方法时,它会将工作内存中的共享变量的最新值刷新回主内存。

八、有序性

有序性指的是程序执行的顺序按照代码的先后顺序执行。JMM通过happens-before原则来保证有序性。以下是一些happens-before规则:

  • 单线程内代码的执行顺序。
  • 锁的获取一定出现在释放之前。
  • volatile变量的写操作happens-before后续的读操作。
  • 线程A启动线程B,线程B的线程启动happens-before线程B的线程终止。
  • 线程A中的解锁操作happens-before线程B中的加锁操作(线程B尝试获取线程A拥有的锁)。

九、案例分析

以下是一个明了的Java程序,分析其内存操作过程:

public class VisibilityTest {

private static boolean flag = false;

public static void main(String[] args) {

Thread t1 = new Thread(() -> {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

flag = true;

System.out.println("t1 set flag to true");

});

Thread t2 = new Thread(() -> {

while (!flag) {

// 循环等待flag变为true

}

System.out.println("t2 see flag is true");

});

t1.start();

t2.start();

}

}

在这个例子中,线程t1修改了共享变量flag的值,而线程t2需要读取这个值。由于flag没有使用volatile关键字修饰,使线程t1对flag的修改也许不会立即对线程t2可见。为了确保可见性,可以将flag声明为volatile:

private static volatile boolean flag = false;

十、总结

Java内存模型是懂得并发编程和线程稳固的基础。通过掌握JMM的概念和原理,我们可以更好地编写并发程序,避免内存可见性和有序性问题。在实际开发中,我们应该遵循JMM的规则,合理使用volatile和synchronized关键字,确保程序的正确性和性能。


本文由IT视界版权所有,禁止未经同意的情况下转发

文章标签: 后端开发


热门