概述
面试中总会被问到JMM内存模型这个问题,但似乎这是一个比较抽象和广泛的问题,因为他不仅包含JAVA底层中关于内存模型的知识,同时还必须了解操作系统。因此要讨论JMM内存模型,首先必须了解操作系统中CPU处理器、缓存(一缓存、二缓、三缓)、寄存器之间的关系。
CPU、高速缓存、寄存器
- (整体流程) cpu处理单元从内存中读取数据,在寄存器进行运算,运算结果重新写会内存
- (速度) 寄存器的速度是最快的,其次是高速缓存区,然后内存。
- 高速缓存:寄存器的存取速度是非常非常快的,内存和寄存器一比,速度太慢。寄存器从内存那存取数据的时候,运算单元只能在那干等了,就会浪费大量的等待时间,所以产生了高速缓存区,寄存器从内存那读取的数据或指令可能会有一部分重复性极高,把这部分数据或指令放到离CPU更近的地方(高速缓存),这样就节省了大量从内存读取的时间。
- 缓存级别:缓存存在于cpu与内存之间,是为了解决cpu从内存读取数据慢。缓存分为一级缓存、二级缓存、三级缓存。。一缓一般32k,与处理器同频最快,主要存储指令和关键数据,二缓一般也与cpu同频,容量从以前的64k到现在的6M。三缓大小有3~12M或更大,速度就相对慢一些,不过也比内存高一些。
当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致。如果真的发生这种情况,那同步回到主内存时以谁的缓存数据为准呢?为了解决一致性的问题,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议来进行操作,这类协议有MSI、MESI(Illinois Protocol)、MOSI、Synapse、Firefly及Dragon Protocol等。
JMM内存模型
JMM内存模型可以抽象为本地内存与共享内存两大区域。本地内存是线程私有的,一个线程有一个独立的本地内存。线程只能直接操作本地内存而不能直接操作共享内存,必须通过本地内存将需要进行读写的数据从共享内存中拷贝到本地内存中,读写操作之后再由本地内存刷新回共享内存。共享内存是多个线程共享的,单线程间不共享本地内存,如果线程间需要通信,必须借助共享内存中转来完成。
原子性如何保证?
原子性:原子性就是要么全部执行成功,要么全部执行失败,不会出现执行一半的情况,原子操作是不可分割的。
Java中的synchronized和Lock就实现了原子性。一个线程在执行某个操作时,其他线程无法进行干扰,必须等待当前线程执行完毕。
可见性
Happens-Before原则
要想保证执行操作B的线程看到线程A的结果,那么A、B线程之间必须满足Happens-Before原则:
- 程序结果顺序原则:无论在一个程序中,代码的执行顺序如何(会发生指令重排),结果是按照代码的顺序生成的不会变。
- 锁规则:无论是在单线程还是多线程的情况下,当一个线程获取锁操作执行完成后释放锁,另一个获取锁的线程必须能够看到前一个获取锁的线程所执行的结果。
- volatile变量规则:如果一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作的结果一定对读的这个线程可见。
- 线程启动规则:在主线程A执行过程中,启动子线程B。当线程B执行之前,线程A的对共享变量的操作结果必须对线程B可见。
- 线程结束规则:在主线程A执行过程中,子线程B终止,那么线程B在终止之前对共享变量的修改结果在线程A中可见。
- 传递性:A happens-before B , B happens-before C,那么A happens-before C
有序性
有序性是指在单线程环境中, 程序是按序依次执行的.
而在多线程环境中, 程序的执行可能因为指令重排而出现乱序.
指令重排
版权声明:本文为qq_39416605原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。