volatile&&单例模式的理解

有什么作用?

两个作用:

1.保证线程可见性

当一个线程对共享变量进行修改的时候,另一个线程要对这个共享数据进行读取或者修改的时候,会发现这个共享变量已经被人修改了

2.禁止指令重排序

首先要知道什么是重排序

当在执行代码的时候,cpu的工作内存会从主存加载到变量进来,当遇到很多的变量都需要进来的时候,因为cpu的工作内存中空间有限,他会将最开始读到的变量写回到主存中,当再次用到这个变量的时候,会重新读取到工作内存,效率会很低。所以就有一个解决方案:

他会重新排序,但是他也不是瞎排序,会基于happey-before原则(在不影响代码执行结果的前提下)才会重排序,这样的话就不用重新从主存读到工作内存了,提高效率。

那既然重排序是好的,那为什么还要禁止指令重排序?

单线程的时候重排序肯定是没问题的,但是多线程的话就会有问题!

现在问大家一个问题,new对象在JVM中会分为哪几步?

1.为new的对象在堆中分配一块内存空间

2.根据这个对象类的构造器,进行初始化,放到刚才分配的堆空间里面

3.栈中的引用指向这个堆中的对象(这一步完成了,那么这个对象就!=null了)

用单例模式(一个程序中只能有却只能有一个对象实例)的代码来举例
public class Singleton {
    
    //volatile的作用是 防止指令重排序,多线程情况下,cpu工作内存重新排序,new出一个半成品的对象,导致报错
    private static volatile Singleton INSTANCE;

    private Singleton(){}

    /**
     * 单例模式中运用的dubbocheck方案
     * 两个if中间加锁
     * @return
     */
    public static Singleton getInstance(){
        //可能由于重排序问题,这个INSTANCE!=null
        if(INSTANCE == null){//这里的第一个if 防止多线程情况下频繁加锁造成的性能问题
            synchronized (Singleton.class){
                if(INSTANCE == null){//这里的if是防止多线程情况下new出多个对象
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

在多线程情况下,new对象的顺序会由于指令重排序,1->3->2(参考上面new对象的步骤),第一个线程可能先执行3再执行2,但是可能还没有执行 2 ,这时候第二的线程进来判断 if INSTANCE!=null,所以会创建出来一个半成品的对象,导致程序报错。

可以解决线程安全吗?

不是线程安全的,因为他不具备原子性

解释一下什么是原子性?

原子性就是一批线程执行任务的时候,不会被其他线程所干扰,要么同时成功,要么同时失败。

比如说在多线程执行i++的操作的时候,实际的结果是少加的情况,具有并发问题,这就是原子性导致的

他是如何保证可见性的?

1. cpu工作内存读取主存后,经过计算后的结果,是通过异步的形式刷到主存中的,被volatile修饰的变量会马上同步刷新到主存中。(MESI协议)

2.总线会马上通知其他cpu内核,变量失效了,其他cpu会重新从主存中读取到最新的数据

为什么不具备原子性?

通过可见性的原理知道,线程改完同步写到主存并且通知其他线程的操作是有延迟的, 他只能尽可能的让其他线程读区到最新的数据,但是阻止不了别人改。所以不具备原子性

他是怎么进行指令重排序的?

内存屏障(面试的时候记住这个词就可以了,里面太深奥了,就算是架构师他也不一定明白原理!)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值