Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/376.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java惰性线程安全单例,使用final字段实现_Java_Concurrency_Singleton_Final - Fatal编程技术网

Java惰性线程安全单例,使用final字段实现

Java惰性线程安全单例,使用final字段实现,java,concurrency,singleton,final,Java,Concurrency,Singleton,Final,我不明白为什么这里需要局部变量: public class FinalWrapper<T> { public final T value; public FinalWrapper(T value) { this.value = value; } } public class Foo { private FinalWrapper<Helper> helperWrapper; public Helper getHelpe

我不明白为什么这里需要局部变量:

public class FinalWrapper<T> {
    public final T value;
    public FinalWrapper(T value) {
        this.value = value;
    }
}

public class Foo {
   private FinalWrapper<Helper> helperWrapper;

   public Helper getHelper() {
      FinalWrapper<Helper> tempWrapper = helperWrapper;

      if (tempWrapper == null) {
          synchronized(this) {
              if (helperWrapper == null) {
                  helperWrapper = new FinalWrapper<Helper>(new Helper());
              }
              tempWrapper = helperWrapper;
          }
      }
      return tempWrapper.value;
   }
}
公共类最终说唱者{
公共最终T值;
公共最终说唱人(T值){
这个值=值;
}
}
公开课Foo{
私人最终说唱人助手说唱人;
公共助手getHelper(){
FinalWrapper tempWrapper=helperWrapper;
if(tempWrapper==null){
已同步(此){
if(helperWrapper==null){
helperWrapper=newfinalwrapper(newhelper());
}
tempWrapper=helperWrapper;
}
}
返回tempWrapper.value;
}
}
我从以下位置获取此代码:。如果没有这个局部变量,会有什么问题?根据维基文章:

Java5中final字段的语义可用于安全地发布helper对象,而无需使用volatile。正确性需要局部变量tempWrapper:简单地使用helperWrapper进行null检查和return语句可能会失败,因为Java内存模型允许读取重新排序。此实现的性能并不一定比volatile实现更好


提前感谢。

要了解根本问题,让我们从代码中删除局部变量:

public class Foo {
    private FinalWrapper<Helper> helperWrapper;

    public Helper getHelper() {
        if (helperWrapper == null) {
            synchronized(this) {
                if (helperWrapper == null) {
                    helperWrapper = new FinalWrapper<Helper>(new Helper());
                }
            }
        }
        return helperWrapper.value;
    }
}
公共类Foo{
私人最终说唱人助手说唱人;
公共助手getHelper(){
if(helperWrapper==null){
已同步(此){
if(helperWrapper==null){
helperWrapper=newfinalwrapper(newhelper());
}
}
}
返回helperWrapper.value;
}
}
在这种情况下,我们有三种解读:

  • 外部空检查
  • 内部空检查
  • 在返回之前读取数据
  • 问题在于,由于读取重新排序,第一次读取可能返回一个非null值,第三次读取可能返回null。这意味着第三次读取发生在第一次读取之前,这将确保
    helperrapper
    已初始化

    添加局部变量解决了这个问题,因为我们将
    helperWrapper
    值分配给
    tempWrapper
    ,然后以什么顺序读取
    tempWrapper
    并不重要。如果它有一个非null值,它将用于null检查和return语句

    之所以会发生这种情况,是因为Java内存模型允许仅出于优化目的对操作进行重新排序。请看以下引用:

    什么是重新排序?

    在许多情况下,访问程序变量 (对象实例字段、类静态字段和数组元素)可以 以与指定的不同的顺序执行 节目。编译器可以自由地对命令进行排序 以优化为名的指令。处理器可以执行 在某些情况下,指示不正常。数据可能是 在内存中的寄存器、处理器缓存和主内存之间移动 与程序指定的顺序不同

    [……]

    编译器、运行时和硬件应该共同创建 似乎是串行语义的错觉,这意味着 单线程程序,程序应该不能观察到 重新排序的影响。然而,重新排序可能在未来几年发挥作用 错误同步的多线程程序,其中一个线程 能够观察其他线程的效果,并且可能能够 检测变量访问对中的其他线程可见 与程序中执行或指定的顺序不同

    [……]


    为了理解根本问题,让我们从代码中删除局部变量:

    public class Foo {
        private FinalWrapper<Helper> helperWrapper;
    
        public Helper getHelper() {
            if (helperWrapper == null) {
                synchronized(this) {
                    if (helperWrapper == null) {
                        helperWrapper = new FinalWrapper<Helper>(new Helper());
                    }
                }
            }
            return helperWrapper.value;
        }
    }
    
    公共类Foo{
    私人最终说唱人助手说唱人;
    公共助手getHelper(){
    if(helperWrapper==null){
    已同步(此){
    if(helperWrapper==null){
    helperWrapper=newfinalwrapper(newhelper());
    }
    }
    }
    返回helperWrapper.value;
    }
    }
    
    在这种情况下,我们有三种解读:

  • 外部空检查
  • 内部空检查
  • 在返回之前读取数据
  • 问题在于,由于读取重新排序,第一次读取可能返回一个非null值,第三次读取可能返回null。这意味着第三次读取发生在第一次读取之前,这将确保
    helperrapper
    已初始化

    添加局部变量解决了这个问题,因为我们将
    helperWrapper
    值分配给
    tempWrapper
    ,然后以什么顺序读取
    tempWrapper
    并不重要。如果它有一个非null值,它将用于null检查和return语句

    之所以会发生这种情况,是因为Java内存模型允许仅出于优化目的对操作进行重新排序。请看以下引用:

    什么是重新排序?

    在许多情况下,访问程序变量 (对象实例字段、类静态字段和数组元素)可以 以与指定的不同的顺序执行 节目。编译器可以自由地对命令进行排序 以优化为名的指令。处理器可以执行 在某些情况下,指示不正常。数据可能是 在内存中的寄存器、处理器缓存和主内存之间移动 与程序指定的顺序不同

    [……]

    编译器、运行时和硬件应该共同创建 似乎是串行语义的错觉,这意味着 单线程程序,程序应该不能观察到 重新排序的影响。H