Java中的JMM模型是建立在硬件的内存模型之上的,它通过一系列规则来限制编译器和处理器对内存的重排序,以保证多线程程序的正确性。JMM模型中最重要的概念是主内存和工作内存。
主内存是Java虚拟机中的一块内存区域,是所有线程共享的。它存储了所有的变量和对象,包括实例字段、静态字段和数组元素。主内存可以被多个线程同时访问。
工作内存是每个线程独有的一块内存区域,它存储了线程需要使用的变量和对象的副本。线程对变量的所有操作都是在工作内存中进行的,而不是直接在主内存中进行。
JMM模型通过一系列规则来描述线程如何与主内存进行交互。其中最重要的规则包括:原子性、可见性和有序性。
原子性保证了一个操作是不可分割的,要么完全执行,要么不执行。JMM模型保证了基本数据类型的读写操作具有原子性。而对于非原子性的操作,可以使用synchronized或者volatile关键字进行同步。
可见性保证了当一个线程修改了变量的值,其他线程能够立即看到这个修改。JMM模型通过在特定的时机刷新变量的值到主内存,以及在特定的时机将主内存中的变量值刷新到工作内存,来实现可见性。volatile关键字可以保证变量的可见性。
有序性保证了程序执行的顺序与代码的编写顺序一致。JMM模型通过禁止特定类型的重排序来保证有序性。volatile关键字和synchronized关键字都可以保证有序性。
下面是一段示例代码,我们来看看JMM模型在实际代码中的应用:
public class JMMExample { private static boolean flag = false; public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> { while (!flag) { // do something } System.out.println("Thread 1 finished"); }); Thread thread2 = new Thread(() -> { flag = true; System.out.println("Thread 2 finished"); }); thread1.start(); Thread.sleep(1000); // 确保thread1先启动 thread2.start(); } }
在这个例子中,我们有两个线程,一个线程不断地检查flag
的值,另一个线程修改flag
的值。我们希望当flag
的值变为true
时,第一个线程能够停止循环并输出"Thread 1 finished"。
但是,由于JMM模型的可见性规则,第一个线程可能无法看到flag
的修改,导致无法停止循环。为了解决这个问题,我们可以将flag
声明为volatile
:
private static volatile boolean flag = false;
这样就能够保证变量的可见性,第一个线程能够正确地看到flag
的修改。
以上是对JMM模型底层实现的简单解析,带上了代码示例。深入了解JMM模型的实现原理对于编写多线程程序非常重要,可以帮助我们避免出现并发相关的问题。