一、众所周知,java的内存模型是一个主内存,每一个线程都有一个工做内存空间,那么主内存同步到工做内存是何时发生的呢?工做内存同步会主内存又是何时发生的呢?java
在cpu进行线程切换时就会发生这些同步吗?那若是是多核cpu呢,多个核心间没有线程切换,那么内存同步是在何时发生的呢?缓存
多个cpu核心共享同一片内存区域,可是cpu的缓存并非共享的,jvm是个虚拟的计算机,把底层抽象化到jvm内部了,那么jvm内部的内存同步是在何时进行的呢?jvm
二、volatile关键的做用到底是什么?spa
已知:一、保证每次对volatile关键字修饰的变量作出的修改,都当即同步到主内存区域中,可是若此时已有其余线程从主内存区域中获取过值并放到本身的工做内存中,这个同步回去的值是不能直接反应到已经获取过这个值的线程中的。线程
二、当线程访问某一个对象时候值的时候,首先经过对象的引用找到对应在堆内存的变量的值,而后把堆内存变量的具体值load到线程本地内存中,创建一个变量副本,以后线程再也不和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完以后的某一个时刻(线程退出以前),自动把线程变量副本的值回写到对象在堆中变量。调试
这个某个时刻在使用volatile修饰的变量上,是当即,其余的不肯定,应该是在线程切换时。对象
三、书上写的是确保这个变量在初始化成一个实例时,多个线程正确的处理这个变量。实例是这样的:内存
假设线程一执行到instance = new SingletonKerriganD()这句,这里看起来是一句话,但实际上它并非一个原子操做(原子操做的意思就是这条语句要么就被执行完,要么就没有被执行过,不能出现执行了一半这种情形)。事实上高级语言里面非原子操做有不少,咱们只要看看这句话被编译后在JVM执行的对应汇编代码就发现,这句话被编译成8条汇编指令,大体作了3件事情: 1.给Kerrigan的实例分配内存。 2.初始化Kerrigan的构造器 3.将instance对象指向分配的内存空间(注意到这步instance就非null了)。 get
可是,因为Java编译器容许处理器乱序执行(out-of-order),以及JDK1.5以前JMM(Java Memory Medel)中Cache、寄存器到主内存回写顺序的规定,上面的第二点和第三点的顺序是没法保证的,也就是说,执行顺序多是1-2-3也多是1-3-2,若是是后者,而且在3执行完毕、2未执行以前,被切换到线程二上,这时候instance由于已经在线程一内执行过了第三点,instance已是非空了,因此线程二直接拿走instance,而后使用,而后瓜熟蒂落地报错,并且这种难以跟踪难以重现的错误估计调试上一星期都未必能找得出来,真是一茶几的杯具啊。 编译器
这里的描述看起来就是在切换线程是主内存与线程内存进行的同步,也就是说volatile还有个做用是防止new了一半被更新到主内存。以及每次使用前都去主内存更新。
三、synchronize关键字知道了
在线程进入synchronized块以前,会把工做存内存中的全部内容映射到主内存上,而后把工做内存清空再从主存储器上拷贝最新的值。而 在线程退出synchronized块时,一样会把工做内存中的值映射到主内存,但此时并不会清空工做内存。这样一来就能够强制其按照上面的顺序运行,以 保证线程在执行完代码块后,工做内存中的值和主内存中的值是一致的,保证了数据的一致性!