关于volatile关键字的理解

这两天面试,问到了DCL的一些问题,就想起了一个平常写DCL中容易忽略的一个关键字volatile,我们知道volatile是个轻量级的synchronized,他主要在多线程开发中保证了共享变量的 “可见性(当一个线程修改一个共享变量时,另外一个线程能够读到这个修改的值;那么 他是怎么做到保证共享变量的“可见性”的呢?

从java语言规范第三版中对volatile的定义如下:

java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排它锁单独获得这个变量。java语言提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明或volatile,java线程内存模型确保所有线程看到这个变量值是一致的。



讨论这些之前,我们先来简单的说一下java中的线程和java内存模型,在java内存模型中,分为了主内存和工作内存,所有的变量都存储在主内存中,每条线程还有自己的工作内存,工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量所有的操作(读取,写入)都必须在工作线程中完成,而不能直接操作主内存中的变量,不同线程之间也无法直接访问对方工作内存中的变量,线程中变量值的传递必须通过主内存。大概用一下图来表示








这就是说 假如主内存中有一个变量值 int i = 0;  当多个线程去修改的时候,他们会把i从主内存拷贝到工作内存,修改完之后再写到主内存当中,我们知道在jvm中,为了提高性能,编译器和处理器会对指令做重排序,从java源码到最终实际执行的指令序列,分别经理下面三种重排序



举个简单的例子

int a = 10;
int b = 2;
int c = a+b;

在这里面  c依赖于ab   但ab之间不存在依赖关系,jvm只保证最后的计算结果是正确的,在执行的时候,有可能执行的是a+b  也有可能执行的是b+a 我们往往在开发过程中有个顺序执行的幻觉,程序是顺序执行的,这个正是因为as-if-serial语义把单线程程序保护了起来,让我们不用担心重排序的问题

那么在多线程的情况下呢?


public class ReorderExample {
   
    int a = 0;
    boolean flag = false;
    public void writer(){
        a = 1;            //1
        flag = true;    //2
    }
    public void reader(){
        if( flag){        //3
            int i = a* a;//4
        }
    }   
}


假如有两个线程A和B,A执行writer方法,随后B执行reader方法,线程B在操作4的时候,能否看到线程A操作1对共享变量a的写入呢?

答案是不一定能看到

原因是操作1和操作2没有数据依赖关系,这两个操作编译器可以对这两个操作进行重排序,同样 3和4也没有数据依赖关系,编译器和处理器也可以对这两个操作进行重排序,他们执行的顺序可能是   2 —> 3 —> 4 —> 1 这么一来,就发生顺序不一致的问题了,但这跟我们主题volatile有什么关系呢?


当声明共享变量为volatile后,这个变量的读写就会比较特别了 

instance = new Singleton();  //instance是volatile变量

通过JIT编译器生成汇编指令来看  如下

0x01a3de1d: movb $0×0,0×1104800(%esi);

0x01a3de24: lock addl $0×0,(%esp);

有volatile修饰的变量,赋值之后会执行 lock addl $0×0,(%esp);的操作,这个操作相当于一个内存屏障,指令重排序的时不能把后面的指令重排序到内存屏障之前的位置;当只有一个CPU访问内存时,并不需要内存屏障,但如果有两个或者多个访问同一块内存的时候,且其中有一个在观测另一个,就需要内存屏障来保证一致性。

我们知道在C/C++上也有这个关键字, 当两个线程都要用到某一个变量且该变量的值会被改变时,应该用volatile声明,该关键字的作用是 防止优化编译器把变量从内存装入CPU寄存器中 。如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行。volatile的意思是 让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值