手记

JMM(Java Memory Model) java 内存模型

带着问题思考:

  1. JMM 是什么?
  2. JMM 存在的意义?
  3. JMM 解决的问题?

内存模型和硬件

内存模型,英文名Memory Model,与内存、CPU 硬件相关。

内存模型其实就是一种规范,保证共享内存的正确性(可见性、有序性、原子性)的规范,
保证共享内存系统中多线程程序读写操作行为的规范。通过这些规则来规范对内存的读写操作,
从而保证指令执行的正确性。它与处理器有关、与缓存有关、与并发有关、与编译器也有关。
解决了CPU多级缓存、处理器优化、指令重排等导致的内存访问问题,保证了并发场景下的一致性、原子性和有序性。

现在的计算机CPU运行都是通过从主内存复制到CPU高速缓存中同步数据,以此来达到快速运行的目的。

  • 单线程

cpu核心的缓存只被一个线程访问。缓存独占,不会出现访问冲突等问题。

  • 单核CPU,多线程

进程中的多个线程会同时访问进程中的共享数据,CPU将某块内存加载到缓存后,不同线程在访问相同的物理地址的时候,
都会映射到相同的缓存位置,这样即使发生线程的切换,缓存仍然不会失效。但由于任何时刻只能有一个线程在执行,
因此不会出现缓存访问冲突。

  • 多核CPU,多线程

每个核都至少有一个L1 缓存。多个线程访问进程中的某个共享内存,且这多个线程分别在不同的核心上执行,
则每个核心都会在各自的caehe中保留一份共享内存的缓冲。由于多核是可以并行的,
可能会出现多个线程同时写各自的缓存的情况,而各自的cache之间的数据就有可能不同。
在CPU和主存之间增加缓存,在多线程场景下就可能存在缓存一致性问题,也就是说,
在多核CPU中,每个核的自己的缓存中,关于同一个数据的缓存内容可能不一致

程序并发的重要概念

  • 原子性 : 一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。

  • 可见性 : 当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

  • 有序性 : 程序执行的顺序按照代码的先后顺序执行。

JMM 是什么

Java内存模型(Java Memory Model ,JMM)

一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,
保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。

Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,
线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,
而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。
而JMM就作用于工作内存和主存之间数据同步过程。他规定了如何做数据同步以及什么时候做数据同步。

JMM 实现

了解Java多线程的都知道,在Java中提供了一系列和并发处理相关的关键字,比如volatile、synchronized、final、concurren包等。
其实这些就是Java内存模型封装了底层的实现后提供给程序员使用的一些关键字。
在开发多线程的代码的时候,我们可以直接使用synchronized等关键字来控制并发,从来就不需要关心底层的编译器优化、缓存一致性等问题。
所以,Java内存模型,除了定义了一套规范,还提供了一系列原语,封装了底层实现后,供开发者直接使用。

在Java中,保证原子性,可见性,有序性

  • 原子性

在Java中,为了保证原子性,提供了两个高级的字节码指令monitorenter和monitorexit。在synchronized的实现原理文章中,介绍过,这两个字节码,在Java中对应的关键字就是synchronized。
因此,在Java中可以使用synchronized来保证方法和代码块内的操作是原子性的。

  • 可见性

Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的这种依赖主内存作为传递媒介的方式来实现的。
Java中的volatile关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次是用之前都从主内存刷新。因此,可以使用volatile来保证多线程操作时变量的可见性。
除了volatile,Java中的synchronized和final两个关键字也可以实现可见性。只不过实现方式不同,这里不再展开了。

  • 有序性

在Java中,可以使用synchronized和volatile来保证多线程之间操作的有序性。实现方式有所区别:
volatile关键字会禁止指令重排。synchronized关键字保证同一时刻只允许一条线程操作。

好了,这里简单的介绍完了Java并发编程中解决原子性、可见性以及有序性可以使用的关键字。
读者可能发现了,好像synchronized关键字是万能的,他可以同时满足以上三种特性,这其实也是很多人滥用synchronized的原因。
但是synchronized是比较影响性能的,虽然编译器提供了很多锁优化技术,但是也不建议过度使用。

2人推荐
随时随地看视频
慕课网APP