volatile可见性,防止重排的应用场景

被volatile修饰之后,那么就具备了两层语义:

1. 可见性:对这个变量进行操作时的可见性

含义: 线程修改的值会立即写入主存,其他同步线程缓存行无效,需从主存中从新读取,那么其线程读取到的就是最新的正确的值,也就保证了修改的对其他线程可见

2.有序性:禁止进行指令重排序

含义: 加入volatile关键字时,会多出一个lock前缀指令,实际上相当于一个内存屏障,在执行到内存屏障这句指令时,在它前面的操作已经全部完成,就防止了处理器改变机器指令的执行顺序。

注意:

1.volatile 不用来修饰数组或对象,用来修饰基本数据类型。

如果只修改里面的属性,不修改它本身,那么volatile关键字是不会对修饰内容起作用的,不能刷新最新值到主缓存中。它仅保证他本身的可见和有序,并不保证内部元素/属性的有序性和可见性。

2.volatile在多线写场景下不保证原子性,不能保证线程安全。

可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性。如自增操作。而且若多线程修改,线程很短写入主存产生冲突,不能保证操作的原子性。

volatile使用场景:

1.运算不依赖变量当前的值。 2.只有单一线程修改变量。3.不与其他变量参与不变约束。
若要保证线程安全可以换用Atomic包下的AtomicReference,AtomicInteger…等包装类。写时复制同步,先比较再更新(CAS语义)。既涵盖了volatile的作用,又保证了原子操作,线程安全,比加锁开销小。

Q:为什么双重校验锁(DCL单例)对象一定要用volatile修饰?

A:虽然双层检查锁确保了线程的安全,但是在创建引用对象时候可能会发生重排,引发空指针异常,所以此处"volatile"关键字不可省略。

public class Singleton
{
    private volatile static Singleton singleton = null;
    private Singleton()  {    }
    public static Singleton getInstance()   {
        if (singleton== null)  {
            synchronized (Singleton.class) {
                if (singleton== null)  {
                    singleton= new Singleton();//可能发生重排
                }
            }
        }
        return singleton;
    }
}
其实singleton= new Singleton()用伪代码展示分为三步:
1.memory=allocate // 分配对象的***内存***
2.ctorInstance(memory) // 初始化对象
3.instance=memory //指向内存***地址***
在2.3.步可能发生重排,若为执行完被其他线程抢占,其他线程进入,此时 singleton==null 但该对象还未被初始化完成,就可能出现空指针的异常。所以单例模式中即使加上双重检查锁也要用volatile修饰。

参考:
https://www.cmsblogs.com/?hmsr=toutiao.io&p=2148

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值