Java内存模型被提出的背景

Java内存模型被提出主要是为解决如下问题

  • 硬件效率问题
  • 计算机内存比CPU慢很多,所以需要在CPU和主存之间加寄存器和高速缓存。
  • 缓存一致性问题
  • 代码指令重排导致多线程执行的“乱序问题”

什么是Java内存模型

Java 虚拟机规范中试图定义一种 Java 内存模型(Java Memory Model,简称 JMM)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果,不必因为不同平台上的物理机的内存模型的差异,对各平台定制化开发程序。

Java内存模型的组成

  • 主内存

    Java 内存模型规定了所有变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件的主内存名字一样,两者可以互相类比,但此处仅是虚拟机内存的一部分)。

  • 本地内存

    每个线程都有自己的本地内存。线程本地内存存有主存中变量的副本。

JMM2

Java内存模型和计算机内存架构

硬件内存架构

JMM3

Java线程与硬件处理器

JMM4

Java内存模型与硬件内存架构的关系

通过对前面的硬件内存架构、Java内存模型以及Java多线程的实现原理的了解,我们应该已经意识到,多线程的执行最终都会映射到硬件处理器上进行执行,但Java内存模型和硬件内存架构并不完全一致。对于硬件内存来说只有寄存器、缓存内存、主内存的概念,并没有本地内存(线程私有数据区域)和主内存(堆内存)之分,也就是说Java内存模型对内存的划分对硬件内存并没有任何影响,因为JMM只是一种抽象的概念,是一组规则,并不实际存在,不管是工作内存的数据还是主内存的数据,对于计算机硬件来说都会存储在计算机主内存中,当然也有可能存储到CPU缓存或者寄存器中,因此总体上来说,Java内存模型和计算机硬件内存架构是一个相互交叉的关系,是一种抽象概念划分与真实物理硬件的交叉。

JMM5

附录

synchronized

Java中的synchronize是使用监视器(monitor)实现的。Java中的每个对象都与一个可以锁定或者解锁的监视器相关联。只有一个线程能够持有某个监视器上的锁,任何其他试图锁定该监视器的线程都将阻塞,直至他们可以获得该监视器上的锁。线程t可以多次锁定某个特定的监视器,而每个解锁操作都会抵消一次锁定操作的效果。

synchronized计算的是对象的引用,然后它试图执行在该对象的监视器上的锁定动作,并且锁定动作成功完成之前不会执行下一步动作。在锁定动作执之后,synchronized语句体被执行。如果该语句体执行结束,无论是正常结束还是猝然结束都会在相同的监视器上执行解锁动作。

synchronized方法在被调用时会自动执行锁定动作。它的方法体在该锁定动作成功完成之前是不会被执行的。如果该方法是实例方法,那么它会锁定与在其上调用该方法的实例相关联的监视器。如果该方法是static的,那么它会锁定与与表示定义该方法的类的Class对象相关联的监视器。

wait、notify和notifyAll

每个对象,除了具有相关联的监视器,还有相关联的等待集,即一个线程集。

当一个对象被创建时,它的等待集为空。向等待集中添加线程或者移除线程的基础动作都是原子性的。等待集只能通过wait、notify、notifyAll方法进行操作

内存模型定义

给定一个程序和该程序的执行轨迹,内存模型可以描述该执行轨迹是否是该程序的一次合法执行。Java编程语言的内存模型是通过如下方式实现的:检查在执行轨迹中的每个读操作。并依据特定的规则,检查该读操作观察到的写操作是否有效。

内存模型描述了程序的可能的行为。Java语言实现可以按照其喜好产生任何代码,只要程序所有的执行过程都会产生内存模型可以预测的结果。

这对Java语言的实现着提供了很大的自由度区执行大量的代码转换,包括冲排序动作和移除不必要的同步。

happen-before

两个动作可以通过happen-before来进行关系排序。如果一个动作在另一个动作之前发生,那么第一个动作对于第二个动作就是可视的,并且第一个动作排在第二个动作之前。