Java的内存模型(JMM)

共享变量:

堆内存在线程之间共享。存储在内存中的所有的实例域、静态域以及数组元素的共享变量。(局部变量,方法定义参数、异常处理器参数不会在线程之间共享,不会有内存可见性问题,不受内存模型的影响)。

JMM定义了线程和主内存之间的抽象关系:

  1)线程之间的共享变量存储在主内存(main memory)中

  2)每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程用以读/写共享变量的副本

  3)本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化

线程A与线程B通信:

  1)线程A把本地内存A中更新过的共享变量刷新到主内存中去

  2)线程B到主内存中去读取线程A之前已更新过的共享变量

JMM通过控制主内存与每个线程的本地内存之间的交互,提供内存可见性保证

 

 

JMM的设计

1)常见的处理器内存模型比JMM要弱,java编译器在生成字节码时,会在执行指令序列的适当位置插入内存屏障来限制处理器的重排序。

2)由于各种处理器内存模型的强弱并不相同,为了在不同的处理器平台向程序员展示一个一致的内存模型,JMM在不同的处理器中需要插入的内存屏障的数量和种类也不相同。

程序员希望:强内存模型编程,易于理解,易于编程。

编译器和处理器希望:弱内存模型,内存模型对它们的束缚越少越好,以提高性能

JMM时的核心目标就是找到一个好的平衡点:一方面要为程序员提供足够强的内存可见性保证;另一方面,对编译器和处理器的限制要尽可能的放松。

JMM把happens- before要求禁止的重排序分为了下面两类:

1)会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止这种重排序。

2)不会改变程序执行结果的重排序,JMM对编译器和处理器不作要求(JMM允许这种重排序)。

  只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。

  比如,如果编译器经过细致的分析后,认定一个锁只会被单个线程访问,那么这个锁可以被消除。

  再比如,如果编译器经过细致的分析后,认定一个volatile变量仅仅只会被单个线程访问,那么编译器可以把这个volatile变量当作一个普通变量来对待。

  这些优化既不会改变程序的执行结果,又能提高程序的执行效率。

顺序一致性内存模型:

顺序一致性内存模型是一个被计算机科学家理想化了的理论参考模型,它为程序员提供了极强的内存可见性保证(JMM没有顺序一致性内存模型保证)。

特性:

1、一个线程中的所有操作必须按照程序的顺序来执行。

2、(不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见。

 

视图信息:

1.顺序一致性模型有一个单一的全局内存

2.在任意时间点最多只能有一个线程可以连接到内存

3.每一个线程必须按程序的顺序来执行内存读/写操作

案例:

线程A:A1->A2->A3  线程B:B1->B2->B3  并发执行

正确同步:

 

两个线程没有做同步:

 

以上可以得出结论:

1、每个线程内部的执行顺序都是按照程序的顺序来执行的。

2、所有的线程都只能看到一个线程整体一致的执行顺序(原因:顺序一致性内存模型中的每个操作必须立即对任意的线程可见)

顺序一致性模型与JMM区别:

  顺序一致性模型保证单线程内的操作会按程序的顺序执行,JMM不保证单线程内的操作会按程序的顺序执行(遵守as-if-serial语义)

  顺序一致性模型保证所有线程只能看到一致的操作执行顺序,而JMM不保证所有线程能看到一致的操作执行顺序

JMM在具体实现上的基本方针:在不改变(正确同步的)程序执行结果的前提下,尽可能的为编译器和处理器的优化打开方便之门。 

正确同步,JMM保证程序的执行结果将与该程序在顺序一致性模型中的执行结果相同(但不保证执行顺序)

int a = 0;
bool flag = false;

public void write() {
    a = 2;              //1
    flag = true;        //2
}

public void multiply() {
    if (flag) {         //3
        int ret = a * a;//4
    }
}

 

假设A线程执行writer()方法后,B线程执行reader()方法

处理器内存模型:

如果完全按照顺序一致性模型来实现,那么很多的处理器和编译器优化都要被禁止,这对执行性能将会有很大的影响。

根据对不同类型读/写操作组合的执行顺序的放松,可以把常见处理器的内存模型划分为下面几种类型:

  1. 放松程序中写-读操作的顺序,由此产生了total store ordering内存模型(简称为TSO)。
  2. 在前面1的基础上,继续放松程序中写-写操作的顺序,由此产生了partial store order 内存模型(简称为PSO)。
  3. 在前面1和2的基础上,继续放松程序中读-写和读-读操作的顺序,由此产生了relaxed memory order内存模型(简称为RMO)和PowerPC内存模型。

注意,这里处理器对读/写操作的放松,是以两个操作之间不存在数据依赖性为前提的(因为处理器要遵守as-if-serial语义,处理器不会对存在数据依赖性的两个内存操作做重排序)。

从上到下,模型由强变弱。越是追求性能的处理器,内存模型设计的会越弱。因为这些处理器希望内存模型对它们的束缚越少越好,这样它们就可以做尽可能多的优化来提高性能。

JMM,处理器内存模型,顺序一致性内存模型之间的关系

JMM是一个语言级的内存模型,处理器内存模型是硬件级的内存模型,顺序一致性内存模型是一个理论参考模型。

语言内存模型,处理器内存模型和顺序一致性内存模型的强弱对比示意图:

 

内存模型越强,越容易保证内存可见性,易编程性就越好。但是重排序就会越少,执行效率就越低。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章