java内存模型与线程-volatile变量的特殊规则

一、规则总结

轻量级的同步机制,变量V为volatile类型。

(1) 在工作内存中,每次使用V前都要先从主内存刷新最新的值,用于保证能看见其它线程对变量V所做的修改后的值。

(2) 在工作内存中,每次修改V后都立刻同步到主内存中,用于保证其它线程看到自己对变量V所做的修改。

(3) 对volatile变量的修改不会被指令重日东月西 ,保证代码的执行顺序与程序的顺序相同。

volatile变量的读性能与普通变量区别不大,但是写操作性能 差一些,然而大多数情况下比加锁要好。

二、两个特点 

当一个变量定义成volatile后,它具备两个特点:

  • 保证此变量对所有线程的可见性

也就是当一个线程修改了这个变量的值,新值对于其它线程来说是可以立马得得知的。而普通变量在值在线程间传递要通过主内存来完成,如,一个线程A修改了一个普通变量,然后向主内存写回,另外一个线程B在线程A回写了后,再从主内存中读取到新的值,新的变量才对线程B可见。

我们可以说volatile变量在各个线程的工作内存中不存在一致性的问题(在各个线程的工作内存中,volatile变量也可以存在不一致的情况,但是由于每次使用前面都进行了刷新,所以看不到一不致的情况,所以我们可以认为不存在一致性问题),但是Java里面的运算并不具备原子性,导致volatile变量的运算在并发情况下也不安全。

例如

package com.company;

/**
 * Created by lsj on 2015/9/7.
 */
public class VolatileTest {
    public static volatile int race =0;

    public static void increase(){
        race++ ;
    }

    private static final int COUNT =20;

    public static void main(String [] args){
        System.out.println("begin");
        Thread [] threads = new Thread[COUNT] ;
        for (int i=0;i<COUNT;i++){
            threads[i]= new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<100;i++){
                        increase();
                    }
                }
            }) ;
            threads[i].start();
        }
        //Thread中yield()是让当前线程暂停,转入就绪状态,
        //让系统的线程调度器重新调度一次,这里已经排队了main线程。
        while (Thread.activeCount()>1){
            Thread.yield();
        }
        System.out.println(race);

    }
}  

输出 的结果 不一定正确 。

问题出在race++中,我们可以得到这一句的字节码:

public static void increase();
  Code :
  Stack =2,Locals=0, Args_size =0
   0:  getstatic    #13;  //Field race:I
   3:  iconst_1
     4:  iadd
    5:  putstatic    #13 ;  //Field race:I
     8:  return

失败的原因:当getstatic将race取到操作栈的时候,volatile 保证了race的值在此时是正确的,但是在执行iconst_1, iadd时,其它的线程可能已经修改了race,而在操作数栈中的值就变成了过期的数据 ,所以putstatic指令就可能只是将不正确的race写入到了内存中。实际上这里的字节码也不一定是原子性的,但是已经可以说明问题了。

由于volatile只能保证可见性,运算场景一定要符合下面的条件才能使用volatile:

(1) 运算结果并不依赖于变量的当前值,或者能够确保只有单一的线程修改变量的值 。

(2) 变量不需要与其它状态 变量共同参与 不变约束。

其它情况的运算场景下我们要加锁处理。

  • 禁止指令重排序优化
时间: 2024-08-04 23:24:44

java内存模型与线程-volatile变量的特殊规则的相关文章

第12章 Java内存模型与线程

1. Java内存模型 Java虚拟机规范中试图定义一种Java内存模型来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致性的并发效果.在此之前,主流程序语言直接使用物理硬件(或者说是操作系统的内存模型),因此会由于不同平台上内存模型的差异,导致程序在一套平台上并发完全正常,而在另一套平台上并发访问却经常出错. 1. 主内存与工作内存 Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节.此处的

Java内存模型与线程 深入理解Java虚拟机总结

在许多情况下,让计算机同时去做几件事情,不仅是因为计算机的运算能力强大了,还有一个很重要的原因是计算机的运算速度与它的存储和通信子系统速度的差距太大, 大量的时间都花费在磁盘I/O.网络通信或者数据库访问上. 如果不希望处理器在大部分时间里都处于等待其他资源的状态,就必须使用一些手段去把处理器的运算能力 " 压榨 " 出来, 否则就会造成很大的浪费,而计算机同时处理几项任务则是最容易想到.也被证明是非常有效的 " 压榨 " 手段. 除了充分利用计算机处理器的能力外,

java内存模型与线程(转) good

java内存模型与线程 参考 http://baike.baidu.com/view/8657411.htm http://developer.51cto.com/art/201309/410971_all.htm http://www.cnblogs.com/skywang12345/p/3447546.html 计算机的CPU计算能力超强,其计算速度与 内存等存储 和通讯子系统的速度相比快了几个数量级, 数据加载到内存中后,cpu处理器运算处理时,大部分时间花在等待获取去获取磁盘IO.网络通

Java并发程序设计(三) Java内存模型和线程安全

Java内存模型和线程安全 一 .原子性 原子性是指一个操作是不可中断的.即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰. 思考:i++是原子操作吗?  二.有序性 Java代码在执行使,并不一点会按照编写程序的语义顺序执行(为了优化性能).具体不做解释. 三.可见性 可见性是指一个线程修改了一个共享变量的值,其他线程能否立即知道这个修改. 编译器优化硬件优化(如写吸收,批操作) Java虚拟机层面的可见性  public class VisibilityTest ext

[笔记]JAVA内存模型与线程

JAVA线程  工作内存  主内存 java内存模型中的八种操作: lock    unlock    read     load     use      assign      store     write 八种基本操作必须满足的规则 volatile 当一个变量被定义成volatile之后,它将具备两种特性 一是保证此变量对所有线程的可见性("可见性"是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的.) 二是禁止指令重排序优化(普通的变量仅仅会保证在该方法

jvm(12)-java内存模型与线程

[0]README 0.1)本文部分文字描述转自“深入理解jvm”,旨在学习“java内存模型与线程” 的基础知识: [1]概述 1)并发处理的广泛应用是使得 Amdahl 定律代替摩尔定律称为计算机性能发展源动力的根本原因: 2)Amdahl 定律:该定律通过系统中并行化与串行化的比重来描述多处理器系统能获得的运算加速能力: 3)摩尔定律:该定律用于描述处理器晶体管数量与运行效率间的发展关系: Conclusion)这两个定律的更替代表了近年来硬件发展从追求处理器频率到追求多核心并行处理的发展

深入理解java虚拟机-第12章Java内存模型与线程

第12章 Java内存模型与线程 Java内存模型  主内存与工作内存: java内存模型规定了所有的变量都在主内存中,每条线程还有自己的工作内存. 工作内存中保存了该线程使用的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行. 内存间交互操作: 1 lock 作用于主内存的变量,它把一个变量标识为一个线程独占的状态. 2 unlock 作用于主内存的变量,把锁定的变量释放出来 3 read 作用于工作内存的变量,把一个变量的值从主内存传输到线程的工作内存中. 4 load 作用于工作

Java内存模型与线程_学习笔记

深入理解java虚拟机: 1.java内存模型 java虚拟机规范中试图定义一种Java内存模型.Java Memory Model(JMM) 1.1 主内存与工作内存 java内存模型规定所有的变量都存储在主内存中(Main Memory)中. 每个线程还有自己的工作内存(working Memory),线程的工作内存保存了该线程使用到的变量的主内存副本拷贝,线程对变量的操作都在工作内存中,而不能直接读写主内存中的变量. 1.2 内存见交互操作 1.3 volatile变量的特殊规则 保证对所

Java 内存模型与线程

when ? why ? how ? what ? 计算机的运行速度和它的存储和通信子系统速度的差距太大,大量的时间都花费在磁盘I/O .网络通信或者数据库访问上.如何把处理器的运算能力"压榨"出来? 如何充分利用计算机处理器? 因为绝大多数的运算任务都不可能只靠处理器"计算"就能完成,处理器至少要与内存交互,如读取运算数据.存储运算结果这个 I/O 操作是很难消除的.又因为存储设备和处理器运算速度有几个数量级差距,所以在内存和处理器之间加了个高速缓存(这样处理器就