java内存模型(JMM)和内存结构经常会被搞混,十个面试的人有九个以为问的是JVM内存结构,其实java的内存模型也是很重要的基础架构,决定了上层技术架构,通常面试官问你 volatile或者synchronized时,那么请注意了,他是想问你内存模型。
在聊java内存模型之前,我们先了解一下服务器的硬件架构和CPU处理模式,这是java内存模型存在的基础。
计算机在运行指令时,都是CPU在执行,执行时不可避免和数据打交道,数据存在主存中。最初问题并不大,但是随着CPU的发展,CPU处理的速度越来越快,导致性能的瓶颈全部集中在了内存的读取上,为了能整体提升性能,在内存上加了一道高速缓存,CPU处理完数据之后放入高速缓存,然后高速缓存再把数据刷入主存。
一般高端点的CPU都有多级缓存L1/L2/L3,线程会先在L1读取数据,不存在就往下递归读取,如果L3也不存在,就会读取主存。
单核多线程,进程中的多个线程会同时访问进程中的共享数据,CPU将某块内存加载到缓存后,不同线程在访问相同的物理地址的时候,都会映射到相同的缓存位置,这样即使发生线程的切换,缓存仍然不会失效。但由于任何时刻只能有一个线程在执行,因此不会出现缓存访问冲突。
多核多线程情况下,每个核心拥有独立的多级高速缓存,每个线程会把本地变量复制一份,放入线程栈,引用的具体对象放在线程堆(同一个地址)。由于变量不可见,当线程执行完成后将数据存在高速缓存,然后刷入主存,这份数据是不可信的,同时,如果代码有根据变量做判断条件,也是不可信的,变量不可见,可能会和判断条件的预期相差很远。
除了这种情况,还有一种硬件问题也比较重要。那就是为了使处理器内部的运算单元能够尽量的被充分利用,处理器可能会对输入代码进行乱序执行处理。这就是处理器优化。
除了现在很多流行的处理器会对代码进行优化乱序处理,很多编程语言的编译器也会有类似的优化,比如Java虚拟机的即时编译器(JIT)也会做指令重排。
可想而知,如果任由处理器优化和编译器对指令重排的话,就可能导致各种各样的问题。
那么什么是内存模型?
Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。
为了保证共享内存的正确性(可见性、有序性、原子性),内存模型定义了共享内存系统中多线程程序读写操作行为的规范。通过这些规则来规范对内存的读写操作,从而保证指令执行的正确性。它与处理器有关、与缓存有关、与并发有关、与编译器也有关。他解决了CPU多级缓存、处理器优化、指令重排等导致的内存访问问题,保证了并发场景下的一致性、原子性和有序性。
Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。
而JMM就作用于工作内存和主存之间数据同步过程。他规定了如何做数据同步以及什么时候做数据同步。
在Java中提供了一系列和并发处理相关的关键字,比如volatile、synchronized、final、concurrent包等。其实这些就是Java内存模型封装了底层的实现后提供给我们使用的一些关键字。
并发编程需要处理的几个点:原子性、可见性、有序性,下一篇文章会总结一下几个关键字对原子性、可见性、有序性的实现。
转载请注明:迷路的老鼠 » 简单聊聊Java内存模型