美文网首页
5.volatile:有序性

5.volatile:有序性

作者: xialedoucaicai | 来源:发表于2018-06-13 16:07 被阅读0次

    1.什么是有序性

    程序按照写代码的先后顺序执行,就是有序的。程序难道还能不按代码顺序执行?这就涉及到CPU的指令重排序问题。

    2.指令重排

    处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。那它如何保证即使重排最终结果也能正确呢?
    答案就是保证指令数据间依赖关系不会被重排所影响。看如下例子:

    int a = 10;    //语句1
    int r = 2;    //语句2
    a = a + 3;    //语句3
    r = a*a;     //语句4
    

    语句2 3 4之间存在相互依赖关系,所以重排的结果一定会保证2->3->4这个顺序,但是1 2顺序就不一定了。
    这种重排在单线程中没有问题,但对于对线程就可能存在问题了,看如下代码:

    //线程1:
    context = loadContext();   //语句1
    inited = true;             //语句2
     
     //线程2:
    while(!inited ){
       sleep()
    }
    doSomethingwithconfig(context);
    

    线程1中语句1 2之间没有依赖,可能会先执行inited=true;这将导致线程2收到错误的消息,使用还未初始化的context。不过我代码是怎么都不能重现,它总是按代码顺序执行,这就没法验证了。
    我们可以为inited加上volatile关键字,这样在对inited修改前,一定会保证之前的代码全部执行完了,就不会出现重排导致的问题了。
    关于指令重排还有很多内容,比如happens-before原则,内存屏障等等,要完全理解可能要深入到硬件,比较复杂和专业,再深入下去感觉要走火入魔,怕钻进去出不来,所以浅尝辄止了,大家有兴趣可以继续深入了解。

    3.最佳实践

    volatile禁止指令重排,最典型的应用应该就是双重锁的单例模式了。看代码

    public class SecondSingleton {
        //volatile关键字保证可见性 同时禁用指令重排(jdk1.5后生效)
        private static volatile SecondSingleton singleton;
        
        private SecondSingleton(){
            
        }
        
        /**
         * 双重检查锁实现单例模式
         * 推荐这样写,既保证线程安全,又延迟加载
         * @return
         */
        public static SecondSingleton getSingleton() {
            if (singleton == null) {
                synchronized (SecondSingleton.class) {
                    if(singleton == null){
                        singleton = new SecondSingleton();
                    }
                }
            }
            return singleton;
        }
    }
    

    为什么要使用volatile修饰singleton?
    主要在于singleton = new SecondSingleton();这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情:

    1. 给 singleton 分配内存
    2. 调用 SecondSingleton的构造函数来初始化成员变量
    3. 将singleton 对象指向分配的内存空间(执行完这步 singleton 就为非 null 了)

    但是由于指令重排,上述第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1->2->3 也可能是 1->3->2。如果是后者,则在 3 执行完毕,2 未执行之前,另一个线程执行if (singleton == null) ,这时 singleton 已经是非 null 了,但却没有初始化,所以该线程会直接返回 singleton ,然后使用,自然就会报错了。

    相关文章

      网友评论

          本文标题:5.volatile:有序性

          本文链接:https://www.haomeiwen.com/subject/kwejeftx.html