Java内存模型

2017-04-22  本文已影响27人  今有所思

Java 虚拟机规范中定义一种Java 内存模型(Java Memory Model, JMM)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。

1. 主内存与工作内存

Java 内存模型的主要目标是定义程序中各个变量的访问规则。此处的变量包括实例字段、静态字段和构造数组对象的元素,但不包括线程私有的局部变量与方法参数等(因为它们不是线程共享的)。

Java 内存模型规定

线程、主内存、工作内存三者的交互关系如图所示。


线程、主内存、工作内存三者的交互关系

2. 内存间交互操作

Java 内存模型定义了 8 种操作来完成主内存与工作内存之间的具体交互协议。虚拟机实现时必须保证每一种操作都是原子的、不可再分的。这 8 种操作分别为:lock、unlock、read、load、use、assign、store、write。

3. volatile 型变量的特殊规则

关键字 volatile 是 Java 虚拟机提供的最轻量级的同步机制。当一个变量定义为 volatile 之后,它将具备两种特性。

1)保证此变量对所有线程的可见性。这里的可见性是指当一个线程修改了这个变量的值,新值对于其他线程来说是立即可以得知的。

由于 volatile 变量只能保证可见性,在不符合以下两条规则的运算场景中,我们仍然要通过加锁来保证原子性。

2)禁止对此变量指令重排序优化。指令重排是指 CPU 采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理。重排的结果看起来依然是有序的。而 volatile 变量的修改会同步到内存中,意味着之前所有的操作都已经执行完成,同时,引起其他 CPU 的 Cache 无效化,使得其他 CPU 立即可见。从而避免由指令重排导致的错误。使用 volatile 关键字可以安全地使用 DCL(双锁检测)来实现单例模式。

public class Singleton {
  private volatile static Singleton instance;
  private Singleton() {}
  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
  public static void main(String[] args) {
    Singleton.getInstance();
  }
}

4. 原子性、可见性与有序性

Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性。volatile 关键字本身包含禁止指令重排序的语义,而 synchronized 则是由于“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则保证了串行地进入。

5. 先行发生原则

先行发生原则是用来判断数据是否存在竞争、线程是否安全的主要依据。如果两个操作之间的关系不包含在下面的规则之中,并且无法从下面的规则推导出来,它们就没有顺序性保障,虚拟机可以对它们随意的进行重排序。

有这样一个结论,时间先后顺序与先行发生原则之间基本没有太大关系,所以我们衡量并发安全问题的时候不要受到时间顺序的干扰,一切必须以先行发生原则为准。

上一篇 下一篇

猜你喜欢

热点阅读