由于在硬件层面存在缓存一致性问题,而缓存一致性会导致可见性问题,处理器的乱序执行会导致原子性问题,指令重排序会导致有序性问题,可见性(volatile的内存屏障可以解决可见性问题),原子性,有序性(指的是在程序运行的过程中代码的执行顺序和我们的编写顺序是不一致的,这是由于编译器的执行重排序,处理器的指令重排序,内存系统的重排序引起的,因为多个线程访问内存的时候,他们之间是没有顺序的,要做到有序就要加锁)都是我们抽象出来的概念,所以在JVM中引入了JMM的概念,这也是个抽象出来的概念。
JMM——Java内存模型
总线锁,缓存锁是CPU层面的解决办法,JMM是CPU和硬件层面(基于物理层面的抽象模型)抽象的一种内存模型。
Java内存模型定义了系统中共享内存在多线程程序读写操作时的规范,用来屏蔽各种硬件和操作系统的内存访问的差异性,跟JVM有点类似,JVM是为了屏蔽操作系统的差异,提供了在操作系统层次上的虚拟机可以真正实现一次编译到处运行的效果。JMM最终是实现Java程序在各个平台上都能达到一致性的内存保证效果。Java内存模型主要定义了程序中各个变量的访问规则,也就是在虚拟机中将变量存储到内存以及从内存中取出变量这些操作的底层细节。通过这些规则来规范对内存的读写操作,保证指令执行的正确性。
Java内存模型与处理器,缓存,并发,编译器都有关,解决了CPU的多级缓存,处理器优化,指令重排序等导致的内存访问问题。
注:变量指的是共享变量,即实例对象,静态字段,数组对象等存储在堆内存中的变量,不包括局部变量,因为他是线程私有的,不属于共享变量。
JMM解决并发问题的方式:
一:限制处理器的优化
二:使用内存屏障
JMM定义了线程和内存的交互方式,在JMM抽象模型中,分为主内存(所有线程共享的)和工作内存(每个线程独有的,相当于栈内存)。
主内存映射堆内存,硬件内存,而工作内存映射高速缓存和寄存器。
线程对变量的读写操作必须在工作内存中进行,不能直接读写主内存的变量,不同的线程之间无法访问对方的工作内存中的变量,线程之间的变量值的传递都需要通过主内存来完成。
线程在访问内存的时候,JMM定义了8种原子操作来执行相应的操作,如Lock,锁定了主内存的变量,保证线程的独占状态:
所以,在操作系统层面上存在的问题在JMM中也存在,只是在Java层面上把它抽象出来以后提供了相应的解决方案。JMM这种规范解决的就是由于多线程通过共享内存进行通信时(线程之间的通信方式:共享内存和消息传递(notify,wait)),存在的本地内存数据不一致,编译器会对代码指令重排序,处理器会对代码乱序执行带来的问题,最终目的是保证并发场景中的原子性,可见性,有序性,而这个问题也是JMM模型本身存在的问题。