HotSpot JVM虚拟机:JAVA内存模型与volatile变量

JAVA虚拟机规范中试图通过“JAVA内存模型”来屏蔽掉各种硬件和各种操作系统队内存访问的差异,让JAVA程序在各种系统平台下都能有一致的并发效果,“一次书写,处处运行”。 
       JAVA内存模型的主要目标:定义程序中各个变量的访问规则,就是在JVM中如何将变量存储到内存和如何从内存中取出变量。这里的变量专指可以被多个线程共享的、存在竞争可能的的实例字段、静态变量和构成数组对象的元素,不包括线程私有的局部变量和方法参数。 
       JAVA内存模型规定了所有变量都在主内存中存储,每个线程都有自己私有的工作内存。工作内存中保存了该线程使用到的变量,这些变量是主内存中对应变量的副本的拷贝,线程对象两种的操作都必须在工作内存中进行,而不能直接读写主内存中的变量。工作内存是相互独立的,即线程A不能直接读取线程B的工作内存,同样,线程B也不能直接读取线程A的工作内存;线程间变量的值只能通过主内存来完成。        
        线程、内存和主内存的关系如下图所示: 

        主内存和工作内存的交互,即如何讲一个变量从主内存拷贝到工作内存、如何从工作内存同步到主内存,JAVA内存模型定义了以下8种操作: 
        lock(锁定):将一个变量标识为被一个线程独占状态。 
        unlock(解锁):将一个变量从独占状态释放出来,释放后的变量才可以被其他线程锁定。 
        read(读取):将一个变量的值从主内存传输到工作内存中,以便随后的load操作。 
        load(载入):把read操作从主内存中得到的变量值放入工作内存的变量的副本中。 
        use(使用):把工作内存中的一个变量的值传给执行引擎,每当虚拟机遇到一个使用到变量的指令时都会使用该指令。 
        assign(赋值):把一个从执行引擎接收到的值赋给工作内存中的变量,每当虚拟机遇到一个给变量赋值的指令时,都要使用该操作。 
        store(存储):把工作内存中的一个变量的值传递给主内存,以便随后的write操作。 
        write(写入):把store操作从工作内存中得到的变量的值写到主内存中的变量。 
        JAVA内存模型只要求read和load、sotre和write这两对操作必须按顺序执行,但没有保证连续执行,也不允许其中其中之一单独执行。 
        
        JAVA内存模型的原子性、可见性和有序性 
        JAVA内存模型主要是建立在如何处理java并发过程中的原子性、可见性和有序性这三个特征的: 
        原子性:java内存模型可以直接通过read、load、assign、use、store和write这六个原子性变量操作来保证。在更大的范围内java是通过lock和unlock操作来满足这种要求,反映在代码层面就是synchronized关键字来保证synchronized块儿中的操作也具有原子性。 
        可见性:就是指当一个线程在工作内存中修改了共享的变量的值后,其他线程能够立即“看到”这个修改。 
        有序性:Java语言提供了volatile和synchronized两个关键字来保证线程间操作的有序性。在java中,有序性可以总结为:如果在本线程内观察,所有操作都是有序的;如果在一个线程内观察另一个线程,所有操作都是无序的。前半句讲的是“线程内表现为串行语义”,后半句指“指令重排序”和“工作内存和主内存同步延迟”。 

        “现行发生(happens-before)原则” 
        “happens-before”先行发生是JAVA内存模型中定义的两项操作之间的偏向关系。现行发生原则是判断数据是否存在竞争,线程是否安全的主要依据。可以这样理解:如果说操作A先行发生于操作B,那么操作A产生的影响线程B能观察到,这个“影响”包括修改了内存中共享变量的值、发消息、调用方法等。 

      
         volatile关键字 
         volatile是Java虚拟机提供的最轻量级的同步机制,JAVA内存模型对volatile专门定义了一些访问规则。 
         当一个变量被定义为volatile后,该变量就拥有了两种特性: 
         1、保证了此变量在内存模型中对所有共享该变量的现成的“可见性”,这个“可见性”是指当一个线程修改了一个volatile变量的之后,新值对于其他线程来说是得知的;而普通的变量不能做到该点,变量值在线程间的同步必须通过主内存来完成,就是说如果线程A修改了一个普通的变量的值,然后向主内存中回写,两外一个线程B在线程A回写完后再从主内存读取该变量,新值才会对线程B可见。 
         2、禁止指令重排序优化。由于java是乱序执行的,普通的变量紧会保证在程序中所有依赖赋值结果的地方正能获得正确的结果,但是不能保证变量赋值的顺序与代码中的顺序一致,这也是JAVA内存模型中描述的“线程内表现为串性的语义”。而volatile是禁止指令重排序优化的。 
         volatile定义的变量并不能保证JAVA内存模型的原子性,但它和synchronized关键字一起可以保证JAVA内存模型的有序性。

http://flyingdutchman.iteye.com/blog/1856802

猜你喜欢

转载自blog.csdn.net/varyall/article/details/82750175