zoukankan      html  css  js  c++  java
  • java 虚拟机指令重新排序

    指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。编译器、处理器也遵循这样一个目标。注意是单线程。多线程的情况下指令重排序就会给程序员带来问题。

    是为了保证单线程的运行效率,可以进行指令优化,不影响单线程的执行效果

    但是指令重排在多线程之中会出现问题

    JVM指令重排导致Singleton双重锁定出错

    public class Singleton {
      private static Singleton instance = null;
      private Singleton() { }
      public static Singleton getInstance() {
         if(instance == null) {
            synchronized (Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();  //非原子操作
                }
            }
         }
         return instance;
       }
    }

    看似简单的一段赋值语句:instance = new Singleton();,其实JVM内部已经转换为多条指令:

    memory = allocate(); //1:分配对象的内存空间

    ctorInstance(memory); //2:初始化对象

    instance = memory; //3:设置instance指向刚分配的内存地址

    但是经过重排序后如下:

    memory = allocate(); //1:分配对象的内存空间

    instance = memory; //3:设置instance指向刚分配的内存地址,此时对象还没被初始化

    ctorInstance(memory); //2:初始化对象

    可以看到指令重排之后,instance指向分配好的内存放在了前面,而这段内存的初始化被排在了后面,在线程A初始化完成这段内存之前,线程B虽然进不去同步代码块,但是在同步代码块之前的判断就会发现instance不为空,此时线程B获得instance对象进行使用就可能发生错误。

    修改为如下的形式:

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

    除了前面内存可见性中讲到的volatile关键字可以保证变量修改的可见性之外,还有另一个重要的作用:在JDK1.5之后,可以使用volatile变量禁止指令重排序。但是上面代码在jdk 1.5之前还是有问题的

    注意,前面反复提到“从语义上讲是没有问题的”,但是很不幸,禁止指令重排优化这条语义直到jdk1.5以后才能正确工作。此前的JDK中即使将变量声明为volatile也无法完全避免重排序所导致的问题。所以,在jdk1.5版本前,双重检查锁形式的单例模式是无法保证线程安全的。

    单例模式最还采用静态内部类的方式来实现,是最经典的也是保证线程安全的

    package com.qqyumidi;
    
    public class SingleTon {
    
        // 利用静态内部类特性实现外部类的单例
        private static class SingleTonBuilder {
            private static SingleTon singleTon = new SingleTon();
        }
    
        // 私有化构造函数
        private SingleTon() {
    
        }
    
        public static SingleTon getInstance() {
            return SingleTonBuilder.singleTon;
        }
    
        public static void main(String[] args) {
            SingleTon instance = getInstance();
        }
    }
  • 相关阅读:
    读书笔记·如何高效读懂一本书
    读书笔记·微习惯
    LittleTools之输出RenderTexture工具
    常用自制脚本(一):透明度统一修改
    LittleTool之批量修改材质
    曲面Shader
    温故而知新之镜头旋转
    LittleTools之网格输出为模型
    Unity给力插件之ShaderForge(三)
    vue的学习
  • 原文地址:https://www.cnblogs.com/kebibuluan/p/7761739.html
Copyright © 2011-2022 走看看