JMM是规范,Java Memory Model
是一组规范,需要各个JVM的实现来遵守JMM规范,以便于开发者可以利用这些规范,更方便地开发多线程程序
如果没有这样的一个JMM内存模型来规范,那么很可能经过了不同JVM的不同规则的重排序之后,导致不同的虚拟机上运行的结果不一样,那是很大的问题。
为啥需要JMM?
1.C语言不存在内存模型的概念
2.依赖处理器,不同处理器结果不一样
3.无法保证并发安全
4.需要一个标准,让多线程运行的结果可预期
重排序、可见性、原子性
1.重排序:在线程1内部的两行代码的实际执行顺序和代码在Java文件中的顺序不一致,代码指令并不是严格按照代码语句执行的,他们的顺序被改变了,它就是重排序,这里被颠倒的y=a和b=1
代码案例
提高处理速度
2.可见性:使用volatile避免可见性问题
导致可见性问题的原因:CPU有多级缓存,导致读的数据过期(高速缓存的容量比主内存小,但是速度仅次于寄存器,所以在CPU和主内存之间多了Cache层;线程间的对于共享变量的可见性问题,不是直接由多核引起的,而是由多缓存引起的;如果所有核心都只用一个缓存,那么也就不存在内存可见性问题;每个核心都会将自己需要的数据读到独占缓存中,数据修改后也是写入到缓存中,然后等待刷入到主存中。所以会导致有些核心读取的值是一个过期的值)
3.原子性:一系列的操作,要么全部执行成功,要么全部不执行,不会出现执行一半的情况,是不可分割的。
用synchronized实现原子性
java中原子操作有:
1.除long和double之外的基本类型(int,byte,boolean,short,char,float)的赋值操作
2.所有引用reference的赋值操作,不管是32位的机器还是64位的机器
3.java.concurrent.Atomic.*包中所有类的原子操作
主内存和本地内存的关系
1.所有的变量都存储在主内存中,同时每个线程也有自己独立的工作内存,工作内存中的变量内容是主内存中的拷贝
2.线程不能直接读写主内存中的变量,而是只能操作自己工作内存中的变量,然后再同步到主内存中
3.主内存是多个线程共享的,但线程之间不共享工作内存,如果线程间需要通信,必须借助主内存中转来完成
所有的共享变量存在于主内存中,每个线程有自己的本地内存,而且线程读写共享数据是通过本地内存交换的,所以才导致了可见性问题