volatile理解及建议

当一个变量被volatile修饰后,会具备两种含义:

1、保证了不同线程对该变量进行操作时的可见性。

2、禁止进行指令重排序


可见性在这里就不做解释了,网上有很多,解释都很到位,有需要了解的可以百度一下。


这里说一下“原子性”,看如下摘抄代码:

public class Test {
    public volatile int inc = 0;
 
    public void increase() {
        inc++;
    }
 
    public static void main(String[] args) {
        final Test test = new Test();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000;j++)
                        test.increase();
                };
            }.start();
        }
 
        while(Thread.activeCount()>1)  //保证前面的线程都执行完
            Thread.yield();
        System.out.println(test.inc);
    }
}


上边的一段代码,包括对inc的读取、操作、赋值三个操作,但其最后输出的值可能并不一样,也可能不是10000,why?

按理说已经保证了inc变量的可见性,双循环每次取值应该都是最新的,10*1000=10000,这很科学啊!


有没有可能自增操作的三个子操作会分割开执行,如果有就可能导致下面这种情况出现:

假如说线程1在做了i+1,但未赋值的时候,线程2就开始读取i,那么当线程A赋值i=1,并回写到主内存,而此时线程B已经不再需要i的值了,而是直接交给处理器去做+1的操作,于是当线程2执行完并回写到主内存,i的值仍然是1,而不是预期的2。


我的理解是,volatile缩短了普通变量在不同线程之间执行的时间差,但仍然存有漏洞,依然不能保证原子性。


同时在这里提一下volatile的使命,创造它的背景就是在某些情况下可以代替synchronized实现可见性,同时规避synchronized带来的线程挂起、调度的开销的目的。如果volatile也能保证同步,那么它就是个锁,可以完全取代synchronized了。

从这点看,volatile不可能保证同步,也正基于上面的原因,随着synchronized性能逐渐提高,volatile逐渐退出历史舞台。


接下来再说一下什么是禁止重排序,这里先说一下什么是重排序,个人理解是,写在后面的代码在时间顺序上可能会先执行,而写在前面的代码会后执行,从而尽可能的充分利用CPU,我们实际应用中,重排序在多线程中出现的几率比较大。

但由于在关键字上有volatile和synchronized可禁用重排序,还有一些规则也是可以禁止重排序的,通过这些使我们在平时在工作中很少能感觉到重排序的体现。



总结一下:

volatile关键字保证了线程间共享变量的及时可见性,但在其整个执行过程并不保证同步,不保证其原子性,但会保证指令不会重新排序。

建议远离volatilevolatile是在synchronized性能低下的时候提出的。如今synchronized的效率已经大幅提升,所以volatile存在的意义不大;并且如今非volatile的共享变量,在访问不是超级频繁的情况下,已经和volatile修饰的变量有同样的效果了。





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值