大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
Java Memory Model简称JMM, 是一系列的Java虚拟机平台对开发者提供的多线程环境下的内存可见性、是否可以重排序等问题的无关具体平台的统一的保证。(可能在术语上与Java运行时内存分布有歧义,后者指堆、方法区、线程栈等内存区域)。
并发编程有多种风格,除了CSP(通信顺序进程)、Actor等模型外,大家最熟悉的应该是基于线程和锁的共享内存模型了。在多线程编程中,需要注意三类并发问题:
·原子性
·可见性
·重排序
原子性涉及到,一个线程执行一个复合操作的时候,其他线程是否能够看到中间的状态、或进行干扰。典型的就是i++的问题了,两个线程同时对共享的堆内存执行++操作,而++操作在JVM、运行时、CPU中的实现都可能是一个复合操作, 例如在JVM指令的角度来看是将i的值从堆内存读到操作数栈、加上一、再写回到堆内存的i,这几个操作的期间,如果没有正确的同步,其他线程也可以同时执行,可能导致数据丢失等问题。常见的原子性问题又叫竞太条件,是基于一个可能失效的结果进行判断,如读取-修改-写入。 可见性和重排序问题都源于系统的优化。
由于CPU的执行速度和内存的存取速度严重不匹配,为了优化性能,基于时间局部性、空间局部性等局部性原理,CPU在和内存间增加了多层高速缓存,当需要取数据时,CPU会先到高速缓存中查找对应的缓存是否存在,存在则直接返回,如果不存在则到内存中取出并保存在高速缓存中。现在多核处理器越基本已经成为标配,这时每个处理器都有自己的缓存,这就涉及到了缓存一致性的问题,CPU有不同强弱的一致性模型,最强的一致性安全性最高,也符合我们的顺序思考的模式,但是在性能上因为需要不同CPU之间的协调通信就会有很多开销。
典型的CPU缓存结构示意图如下
CPU的指令周期通常为取指令、解析指令读取数据、执行指令、数据写回寄存器或内存。串行执行指令时其中的读取存储数据部分占用时间较长,所以CPU普遍采取指令流水线的方式同时执行多个指令, 提高整体吞吐率,就像工厂流水线一样。
读取数据和写回数据到内存相比执行指令的速度不在一个数量级上,所以CPU使用寄存器、高速缓存作为缓存和缓冲,在从内存中读取数据时,会读取一个缓存行(cache line)的数据(类似磁盘读取读取一个block)。数据写回的模块在旧数据没有在缓存中的情况下会将存储请求放入一个store buffer中继续执行指令周期的下一个阶段,如果存在于缓存中则会更新缓存,缓存中的数据会根据一定策略flush到内存。
public class MemoryModel { private int count; private boolean stop; public void initCountAndStop() { count = 1; stop = false; } public void doLoop() { while(!stop) { count++; } } public void printResult() { System.out.println(count); System.out.println(stop); } }