JMM介绍
JMM是什么 呢?它是Java内存模型。我们都知道每个程序都有很多线程组成,既然出现线程必然会出现意想不到的问题。 出现线程安全问题一般是因为主线程和工作线程数据不一致和重排序导致的。所以,要想找到根本原因就需要理解Java内存模型。
Java中哪些是共享变量
在Java程序中所有的实例方法,静态方法和数组元素都放在堆内存中,这些就是可以共享的,所有的线程都可以访问到。所以,共享数据就出现安全问题。局部变量就是方法定义参数和异常处理参数,不会被共享访问。
工作内存和主内存
所以的变量都放在主内存中,所有的线程都有自己的工作内存。工作内存存储在寄存器或高速缓存中。保存了该线程使用的变量的主内存副本拷贝。 线程只能直接操作工作内存中的变量,不同线程的之间的变量传递是通过主内存完成的
内存模型的三大特性:原子性,可见性,有序性。
原子性:可以通过synchronized 来实现 可见性: volatile,通过在指令中添加lock指令,以实现内存可见性。 synchronized,当线程获取锁时会从主内存中获取共享变量的最新值,释放锁的时候会将共享变量同步到主内存中。 final,被 final 关键字修饰的字段在构造器中一旦初始化完成,并且没有发生 this 逃逸(其它线程通过 this 引用访问到初始化了一半的对象),那么其它线程就能看见 final 字段的值。 有序性:指的是线程都是有序的。
内存屏障
如果要阻止重排序要怎么办呢?加一个内存屏障。
内存屏障主要有四个:
volatile写是在前面和后面分别插入内存屏障,而volatile读操作是在后面插入两个内存屏障
StoreStore屏障:禁止上面的普通写和下面的volatile写重排序;
StoreLoad屏障:防止上面的volatile写与下面可能有的volatile读/写重排序
LoadLoad屏障:禁止下面所有的普通读操作和上面的volatile读重排序
LoadStore屏障:禁止下面所有的普通写操作和上面的volatile读重排序
Java编译器会在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。为了实现volatile的内存语义,JMM会限制特定类型的编译器和处理器重排序,JMM会针对编译器制定volatile重排序规则表: