程序中的变量都存储在主内存中,而每个线程拥有自己的工作内存用来存放变量的拷贝,线程的读写操作是在各自的工作内存中进行的,操作的对象都是变量的拷贝,操作完毕后在刷新到主内存,JMM就是规定了工作内存和主内存之间变量访问的细节,通过保障原子性、有序性、可见性实现线程安全。
JVM在运行时把内存划分成多个功能区,每个区域对应着不能的存储内容,生命周期,共享性质,GC策略等。可以看到,能被线程共享的是方法区和堆中的数据,也就是实例对象、数组和静态变量,这些共享数据受到JMM规范影响。而局部变量、方法参数、异常处理参数都在虚拟机栈中,这些数据为线程私有的,所以不受JMM规范影响。
原子性 数据库事务中也有原子性的定义,即一个操作是不可被中断的,要么全部成功,要么全部失败。
JMM中的原子操作是指一个操作不会被线程调度机制打断,一旦开始,就一直运行到结束,中间不会有任何线程切换(context switch)原子性可以保障读取到的某个属性的值是由一个线程写入的。 变量不会在同一时刻受到多个线程同时写入造成干扰。
如在32位的JVM中对64位long 或double 值的写操作是分成两次相邻的32位值写操作,在多线程的环境下,可能会有线程只读到了前32位,这种操作就是非原子性的,非原子性操作会受到多线程的干扰而产生结果混乱。
基本类型的单次读写操作是原子的,但是复合操作如,int i=0;i++,就是非原子性的。
JMM解决原子性的方式,volatile语义(保证变量单次操作的的原子性)、锁语义。
有序性 现代CPU的计算速度远远高于内存的读写速度,CPU会采用高速缓存来抵消内存访问带来的延迟。甚至高速缓存也分成多级,最快的离CPU最近。但是速度还是远远低于CUP指令执行的速度,为了减少CACHE_WAIT,CPU会采用指令级并行重排序来提供执行效率,也可以叫做CPU乱序执行。
CUP的高速缓存与内存之间不是实时同步的,高速缓与高速缓间也不是实时同步,而是通过缓存一致性协议(MESI)将数据新到主内存,缓存和读写缓冲区之间也会通过指令重排序来优化数据的刷新。
JIT编译器也会在代码编译的时候对代码进行重新整理,最大限度的去优化代码的执行效率。
|
|