04-Java内存模型简介

1.多线程下的两个问题

在Java多线程通信中会存在两个让人头痛的问题,分别是内存可见性、有序性。

2.Java内存模型

    Java内存模型是一个抽象的概念(简称JMM),它主要给内存可见性与有序性做了保证,Java线程之前的通信细节都由JMM控制,JMM定义了线程与主内存之间的抽象模型,线程之间的共享变量是存放主内存模型,每个线程有个私有的本地空间,本地空间存储的是该线程读/写的变量副本。如下图

04-Java内存模型简介_第1张图片

3.内存可见性

    线程A与线程B通信,如果没有JMM控制会出现内存可见性问题,假设主内存共享变量i=0,A线程从主内存读取i变量到本地内存A,接着在本地内存修改i=1,此时线程B去主内存获取到的共享变量i依然还是0,如果线程A与B通信要解决内存可见性的问题需要依靠JMM的控制,JMM控制必须要经过下面两个步骤

  1. 线程A把本地内存的共享变量刷新到主内存上去。
  2. 线程B去主内存读取线程A之前已经更新过的共享变量。

如下图

04-Java内存模型简介_第2张图片

4.重排序

在执行程序时,为了提高性能,编译器与处理器常常会对指令做重排序,重排序分三种类型

  1. 编译器优化重排序–编译器在不改变单线程的结果为前提下,可以重新安排语句的执行顺序(as-if-serial)。
  2. 编译器的重排序–现代处理器采用了指令集并行技术来将多条指令重叠执行,前提是数据不存在依赖性(as-if-serial)。
  3. 内存系统的重排–因为处理器内的操作已经重排,导致加载进内存也是无序的。

    上述1是属于编译器重排,2与3是处理器重排,这些重排可能会导致多线程执行的结果不正确,JMM对这些编译器与处理器重排尽可能的加入内存屏障禁止部分重排序,换句话说JMM在保证正确同步的结果为前提下,做尽可能的优化。

    as-if-serial语义的意思是说,不管怎么重排序都不会影响的程序的结果,编译器、处理器都必须遵守as-if-serial。为了遵守as-if-serial,编译器、处理器都不会对存在依赖性的数据做重排序,因为这样会改变程序运行结果,数据依赖类型表共有如下三种

  1. 写后读 a=1 b=a
  2. 写后写 a=1 a=2
  3. 读后写 a=b b=1

    注意的是数据的依赖性只针对单个处理器中执行的指令序列与单个线程中执行的操作。不同处理器和不同线程之间的数据依赖性不被编译器与处理器考虑。

5.内存可见性、内存屏障的手段

    JMM要保证有序性与内存可见性就需要一些手段,这些手段分别是Volatie、锁、happens-before,这些手段分会单独写个文章细说。

6.顺序一致性内存模型

顺序一致性内存模型是被理想化的参考模型,它提高了极强的内存可见性。顺序一致性模型有三大特征

  1. 一个线程中的所有操作必须按照程序的顺序来执行。
  2. 所有线程只能看到单一的操作顺序。
  3. 每个操作都是原子的,执行完后立刻对所有线程可见。

注意:JMM不保证未同步或未正确同步的执行结果与该程序在顺序一致性模型中的结果一致

7.内存模型之间的关系

    JMM是个语言级的内存模型,处理器是硬件级别的内存模型,顺序一致性是理论参考模型,我们在设计内存模型的时候都是会参考顺序一致性模型,在设计时会对顺序一致性模型执行顺序做一定的放松,如果完全按照顺序一致性模型设计,很多处理器与编译器优化都会被禁用,这对执行的性能会有很大的影响。越是追求性能的内存模型,它的设计会弱。(在保证程序正确结果前提下)

    一般语言模型与硬件模型会比一致性模型弱,硬件模型会比语言模型弱,所以JMM在不同的处理器上会插入不同的内存屏障来做禁止重排,JMM屏蔽了不同处理器模型的差异,为Java程序员提供了一个一致的内存模型,如下图

04-Java内存模型简介_第3张图片

参考:

《Java并发编程的艺术》

你可能感兴趣的:(java多线程)