java的内存模型,简称JMM
什么是java的内存模型? java的内存模型就是一种符合内存规范的,屏蔽了各种硬件和操作系统的访问差异,保证了java程序在各种平台下对内存的访问都能保证效果一致的机制和规范。
java内存模型的主要目的是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出这个变量的内存细节。
JMM规定了所有变量都存储在主内存中,每个线程还有自己的工作内存,线程的工作内存保存了线程使用到的变量的主内存的拷贝,线程对变量的所有操作(读取、赋值等)都在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间也无法访问对方的工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
所以,JMM其实是一种规范,解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。
JMM围绕可见性、原子性、有序性来实现的:
1,原子性
一个操作不会被cpu多线程切换打断,要么全部执行,要么不执行。
注意:
在32位虚拟机中,long和double都不具有原子性; 所有基本数据类型的++操作也不具有原子性。
2,可见性
一个线程对共享变量做了修改后,其他线程可以立刻看到该变量的这种修改。
实现可见性的方式:
1,volatile
2,synchronized
3,Lock
4,final
3,有序性
为了提高程序性能,编译器和处理器常常会对指令进行重排。指令重排分为三种类型: 1,编译器优化的重排序,编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺,序;
2,指令级并行重排序,现代处理器采用了指令级并行技术来将多条指令并行执行,如果不存在数据依赖关系,处理器可以改变语句对应指令的执行顺序;
3,内存系统的重排序,由于处理器使用缓存和读/写缓冲区,这样使读写操作看上去像是无序的。
上图1中属于编译器重排序,2和3属于处理器重排序。这些重排序可能会导致多线程之间出现可见性的问题。对于编译器,JMM会禁止特定的编译器进行重排序。对于处理器重排序,JMM会插入特定类型的内存屏障指令,通过内存屏障可以禁止指令重排序。
为了保证内存可见性,java编译器会插入特定类型的内存屏障指令来禁止处理器重排序。JMM里将内存屏障分为以下四类:
StoreLoad Barriers 是一个全能屏障,他同时具备其他三个屏障的效果。该屏障的性能比较低,因为执行该屏障指令时处理器需要将写缓冲区的数据全部刷新到内存中(buffer fully flush)。
happens-before
JMM中存在一些“天然性”的先行发生关系,这些关系不需要任何同步协助,可以在编码中直接使用。
1,程序次序规则:在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于写在后面的操作;
2,管程锁定规则:在同一个锁的前提下,一个unlock操作先行发生于后面对这个锁的lock操作;
3,volatite变量规则:对volatile变量的写操作要先行于对这个变量的读操作;
4,线程启动规则:Thread对象的start()方法先行发生于这个线程的每一个动作;
5,线程终止规则:线程中所有的操作都先行发生于对此线程的终止检测。可以通过Thread.join()方法结束、Thread.isAlive()来检测线程是否已经终止;
6,线程中断规则:对线程interrupt()的方法调用先行发生于中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()检测是否发生中断;
7,对象终结规则:一个对象的初始化完成(构造方法执行完成)先行于finalize()方法的开始;
8,传递性