Java内存模型(JMM)定义了Java虚拟机(JVM)在计算机内存中的工作方式,特别是关于共享内存的并发操作。JMM解决了两个基本问题:线程间的可见性和重排序问题。可见性指的是一个线程对共享变量修改的结果什么时候对另一个线程可见;重排序是指编译器和处理器为了优化程序性能而做的指令序列调整。
JMM的主要特点
-
可见性(Visibility)
可见性保证一个线程对共享变量的修改,最终会对其他线程可见。Java提供volatile
关键字来保证可见性。 -
原子性(Atomicity)
原子性是指一个操作是不可中断的,即使是在多线程同时执行的情况下。在Java中,原子性主要是通过synchronized
关键字和java.util.concurrent.atomic
包下的原子类来实现。 -
有序性(Ordering)
有序性即程序执行的顺序按照代码的先后顺序执行。Java中,volatile
关键字和happens-before
原则是保证操作有序性的主要手段。
JMM与工作内存和主内存交互
JMM定义了线程和主内存之间的抽象关系。线程间的共享变量存储在主内存中,每个线程都有一个私有的本地内存(工作内存),本地内存保存了该线程使用到的变量的主内存副本拷贝。
线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不是直接对主内存操作。不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
内存间交互操作
JMM定义了8种操作来完成主内存与工作内存之间的交互:
lock
:作用于主内存的变量,它标识一个变量被某个线程独占。unlock
:作用于主内存的变量,它标识一个变量由锁定状态变为解锁状态。read
:作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中。load
:作用于工作内存的变量,它把read
操作从主内存中得到的变量值放入工作内存的变量副本中。use
:作用于工作内存的变量,它把工作内存中的一个变量的值传递给执行引擎。assign
:作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量。store
:作用于工作内存的变量,它把工作内存中的一个变量的值传送到主内存中。write
:作用于主内存的变量,它把store
操作从工作内存中得到的变量的值放入主内存的变量中。
这些操作必须是原子性的,不可再分的(对于read
和load
,store
和write
来说是成对的)。
volatile关键字
volatile
是JMM的关键字,用于标记Java变量,确保此变量对所有线程的可见性。volatile
变量的读/写操作与锁有相似的内存语义,即每次读取时都从主内存读取,每次写入时都写入主内存。
public class VolatileExample {
private volatile boolean active;
public void run() {
while (active) {
// do something
}
}
public void stop() {
active = false;
}
}
在上面的示例中,active
变量被volatile
修饰,确保了run
方法中对该变量状态的读取总是从主内存中获取最新的值,而stop
方法中的写入也会立即反映到主内存中。
synchronized关键字
synchronized
关键字可以保证方法或代码块一次只能被一个线程执行。此外,它还能保证进入同步代码块时将变量的最新值从主内存读到工作内存中,退出时将工作内存的变量状态刷新回主内存。
public class SynchronizedExample {
private int sharedState;
public synchronized void increment() {
sharedState++;
}
public synchronized int getSharedState() {
return sharedState;
}
}
在这个示例中,increment
方法用于增加sharedState
的值,getSharedState
方法用于获取sharedState
的值。由于这两个方法都被synchronized
修饰,所以对sharedState
的所有访问都是同步的。
happens-before原则
JMM定义了happens-before原则来确定程序中不同操作间的偏序关系:
- 程序顺序规则(Program Order Rule)
- 监视器锁规则(Monitor Lock Rule)
- volatile变量规则(Volatile Variable Rule)
- 线程启动规则(Thread Start Rule)
- 线程终止规则(Thread Termination Rule)
- 中断规则(Interruption Rule)
- 终结器规则(Finalizer Rule)
- 传递性规则(Transitivity Rule)
总结
JMM是一个抽象的概念,它定义了一个线程如何通过主内存与其他线程进行交云,以及线程如何和主内存协作从工作内存同步数据,以达到线程安全。JMM通过volatile、synchronized关键字,以及happens-before原则为程序员提供了工具和规则来编写多线程程序。
在编写多线程程序时,确保正确使用JMM的规则和提供的关键字至关重要,因为这能够帮助防止内存一致性错误,保证程序的并发性和正确性。