(1)高速缓存
使读写速度尽可能快
在多处理器系统中,每个处理器都有自己的高速缓存,而他们又共享统一主存
缓存中数据不一致问题
有一些协议
(2)指令重排序
处理器对输入的代码进行乱序执行优化,处理器在计算之后将乱序执行的结果重组,保证乱序执行的结果和顺序执行的结果是一致的
Java内存模型JMM
主内存(main memory)
工作内存(working memory)
屏蔽硬件和操作系统的内存访问差异,是Java程序在任何平台下都达到一致性的访问效果
定义变量从内存中取出以及储存到内存的细节
变量指的是共享变量(包括实例字段、静态字段、构成数组的元素,不包括局部变量与方法参数这些线程私有的变量)
所有的共享变量都存储在主内存中
每条线程都拥有自己的工作内存
线程的工作内存中保存了当前线程使用到的共享变量的主内存副本拷贝
线程不能直接操作主内存
一个线程也不能访问另一个线程的工作内存
主内存和线程工作内存的交互操作
lock unlock(作用于主内存)
read write(主内存)
load store(工作内存)
use assign(工作内存)
上述操作都是原子性的
(1)read之后必须load,store之后必须write(不允许一个变量从主存读取了,但是工作内存不接受;或者一个变量从工作内存发起了回写,但是主存不接受)
(2)不能丢弃assign(不允许一个线程在其工作内存中修改了变量的值,但是又没有同步回主存;也不允许一个线程没有assign变量,但是又想同步回主存)
(3)不允许在工作内存中新建变量(工作内存中use和assign的变量必须是从主从中read并load到的)
(4)同一时刻,只允许一条线程对一个变量进行lock,但是,允许一个线程执行多次lock。每一个lock都必须有与之对应的unlock
(5)在unlock之前,必须把变量同步回主存
volatile关键字
JVM提供的轻量级同步策略
(1)可见性:保证主存中的共享变量对所有线程都是可见的,即当一个线程改变了变量的值,其它线程立即可知
在各个线程的工作内存中,volatile变量可能存在不一致的情况,但是,由于每次使用之前执行引擎都要刷新volatile变量的值,因此看不到不一致的情况
不能保证原子性(i++)
(2)禁止指令重排序
内存屏障
在指令重排序时,不能把内存屏障后面的指令重排序到内存屏障之前
性能:读操作跟普通变量差不多
写操作慢一些
(1)每次使用(use)volatile变量的时候,都必须先从主内存中load最新的值,用于保证当前线程看见其它线程对共享变量所做的修改
(2)每次修改(assign)volatile变量之后,都必须立即将修改同步回主内存(store),用于保证其它线程能看见当前线程对共享变量所做的修改
(3)禁止指令重排序,代码的执行顺序和程序中定义的顺序是一致的
long&double变量
long和double变量的非原子性协议
允许虚拟机将没有被volatile修饰的64位数据类型的读写操作分为两次32位的操作进行
即64位数据类型不用保证read、load、store、write操作的原子性
(1)原子性
read load use assign store write 操作都是原子性的
long double类型的特殊规定
(2)可见性
一个线程对共享变量的修改,其它线程立即可知
volatile保证可见性:修改数据之后,立即同步回主存;使用数据之前,从主存中刷新最新值
synchronized保证可见性:对一个变量unlock之前,必须同步回主存
final保证可见性:final字段在构造器中一旦初始化完成,构造器没有把this引用传递出去,其它线程中就可看见final变量的值
(3)有序性
在本线程内观察,所有操作都是有序的;但是,在线程外观察,所有操作都是无序的
线程内表现为串行的语义
指令重排序
工作内存和主内存的同步延迟
volatile保证有序性:禁止指令重排序(内存屏障)
synchronized保证有序性:同一时刻只允许一条线程获得对象锁
内存可见性问题
指令都是在CPU中执行的,指令在执行过程中进行数据 的读取和写入
程序执行过程中的临时数据是存放在主存中的
线程在使用主存中的共享数据时,会复制一份到高速缓存中,对共享数据操作完之后,再将数据写入告诉缓存
多个线程对共享数据的操作是在线程自己的缓存中进行的,线程之间彼此不可见