由于在面试中也问到了JMM,于是就记录一下。首先我们要清楚什么是JMM内存模型。在JMM中,所有的变量都是存储在主内存(此处变量包括类变量、实例、静态字段等,但是不包括局部变量和方法参数)。每个线程有自己的工作内存,工作内存中保存了该线程使用的变量的主内存副本。线程对所有变量的操作必须在工作内存中完成,而不能直接读写主内存中的数据。不同的线程之间也无法直接访问对方工作内存中的变量。线程间变量值的传递需要通过主内存来完成。

JMM

用于屏蔽各种硬件和操作系统的访问差异,以实现Java程序在各种平台下都能达到一致的内存访问效果。
是围绕着在并发过程中如何处理原子性、可见性、有序性这三个特征来建立的。
目的是定义程序中各种变量的访问规则,即关注在虚拟机中把变量存储到内存和从内存中取出变量这样的细节。

原子性

Java内存模型中直接保证原子性变量的操作有以下几种:

lock(锁定):作用于主内存的变量,它把一个变量标识为一个线程独占的状态。
unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来。
read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中。
load(载入):作用于工作内存中的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存中的变量,它把工作内存中的一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收的值赋给工作内存中的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存中的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write使用。
write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

如果要把一个变量从主内存拷贝到工作内存中,就要按顺序执行read,load操作。
如果要把变量从工作内存同步回主内存,就要按顺序执行store和write操作。

可见性

指的是一个线程修改了共享变量的值时,其他线程能够立即得知这个修改。是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式实现可见性的。

有序性

单线程中天然有序,多线程中可能因为指令重排序的原因会发生某些错误。如果是变量可以用volatile,如果是代码块等可以用synchrnized禁止指令重排序。

Volatile关键字

volatile可以说是Java虚拟机提供的最轻量的同步机制。volatile的特性有:

保证有序性:通过禁止指令重排序来实现。
保证可见性:工作内存读取变量前先刷新,变量修改后通知。
但是不能保证原子性:指的是符合操作的原子性,例如i++

问题:volatile是如何保证可见性、有序性、举个不能保证原子性的例子出来?

答:volatile变量每次被线程访问时,都被迫从主内存中重读该变量得值,而当该变量发生变化时,又会将最新得值刷新会主内存中。写操作之前插入storestore屏障,store2写入操作执行前,保证store1写入操作对其他处理器可见,写完后插入storeload屏障,load2读取操作前,保证store1得写入对所有处理器可见。读操作前插入loadload屏障,load2读取前要保证load1读取完毕。读操作之后插入loadstore屏障,store2写入前保证load1读取数据完毕。