volatile的作用

volatile的作用

public class LazySingleton {
    //volatile 防止指令重排
    private volatile static LazySingleton instance;

    private LazySingleton(){

    }

    public static LazySingleton getInstance(){
        if(instance == null){
            synchronized (LazySingleton.class){
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

我们进行了两次 if (singleton == null) 检查,这种写法是可以保证线程安全的,假设有两个线程同时到达 synchronized 语句块,那么实例化代码只会由其中先抢到锁的线程执行一次,而后抢到锁的线程会在第二个 if 判断中发现 singleton 不为 null,所以跳过创建实例的语句。再后面的其他线程再来调用 getInstance 方法时,只需判断第一次的 if (singleton == null) ,然后会跳过整个 if 块,直接 return 实例化后的对象。
这种写法的优点是不仅线程安全,而且延迟加载、效率也更高。
volatile 在其中又起到什么作用呢?

public class Main {

    public static void main(String[] args) {
	// write your code here

        Book book = new Book();
    }
}

这Main函数在字节码层做了哪些操作呢,我们对Main函数的Class文件进行反编译(javap -v Main.class)
在这里插入图片描述
1.new会在堆空间里开辟一个空间,并将该空间的引用地址返回,存入到栈中。
2.然后dup指令为复制操作数栈顶值,并将其压入栈顶,也就是说此时操作数栈上有连续相同的两个对象地址;
3.invokespecial指令调用实例初始化方法<init>:()V,注意这个方法是一个实例方法,所以需要从操作数栈顶弹出一个this引用,也就是说这一步会弹出一个之前入栈的对象地址;
4.此时栈中还有一个对象的引用地址,astore_1指令将引用地址赋值给对应的变量

Book book = new Book() 并非是一个原子操作。
事实上,在 JVM 中上述语句至少做了以下这 3 件事::1.分配控制2.初始化 3.引用赋值

例如:在单列模式中此时有两个线程,当第一个线程获取锁后,判instance是否为空,此时还未有则new一个,而恰巧此是程序发生了指令重排2,3步骤发生调换,先赋值后初始化,而在赋值后,还未初始化时,第二个线程判断instance是否空,以为该变量已经赋值,则返回Ture,返回单例对象,但此对象还未进行初始化,则程序将有可能引发空指针等错误。

此时给instance 添加volatile 就是为了防止JIT 、CPU等进行指令重排,相当于是表明了该字段的更新可能是在其他线程中发生的,因此应确保在读取另一个线程写入的值时,可以顺利执行接下来所需的操作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值