Java内存模型(JMM)

线程之间的通信机制有两种:共享内存和消息传递。
Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式进行,整个通信过程对 程序员完全透明

Java内存模型

  • 主要目标: 定义程序中各个变量的访问规则
    (变量指的是 实例字段,静态字段,数组对象元素等非线程私有的)
  • 用处 : 屏蔽各硬件与系统对内存访问的差异
Java内存模型(JMM)_第1张图片
image.png

JMM保障原子性、有序性、可见性来实现线程的有效协同和数据的安全。

模型规定

  1. 所有变量存储在主内存
  2. 每条线程拥有自己的工作内存

重排序

重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

从Java源代码到最终实际执行的指令序列,会分别经历下面3种重排序,如下图所示


image.png

如果两个操作访问同一个变量,且这两个操作中有一个为写操作,
此时这两个操作之间就存在数据依赖性

Java内存模型(JMM)_第2张图片
image.png

对于存在数据依赖性的两个操作,不能重排序

为什么说重排序能优化性能?

让相关联的代码一起。
比如

  int a = 1;
  // 注释处有一堆与变量a无关的代码
  int temp = a;

上面与下面的代码性能上可能有差距,因为下面的a可能在寄存器中就能命中,而上面的不行

  int a = 1;
  int temp = a;
  // 注释处有一堆与变量a无关的代码

Volatile

volatile写的内存语义如下。
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。

volatile读的内存语义如下。
当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

  • 可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
  • (不保证)原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。
  • 禁止重排序优化

在double check 单例模式中, 应该使用volatile标记单例
因为创建对象的过程并不是原子的,他包括
1.给对象的实例分配内存
2.按顺序执行对象的构造器
3.将instance对象指向分配的内存空间(注意 此时instance就不为空)
若重排序为1 3 2 , 则有可能出现问题, 因此要加入volatile

-- 文章内容转载自网络

  • JMM不保证单线程内的操作会按程序的顺序执行
  • JMM不保证所有线程能看到一致的操作执行顺序

实际上,JMM保证的是多线程程序中的每个线程的自身执行时正确的,有序的,而不保证多个线程是正确的,多线程程序的正确性要靠程序员的正确同步来保证。

你可能感兴趣的:(Java内存模型(JMM))