我们真的需要在本地复制this.myVolatile来执行Java 8中的同步双if模式吗?

我们真的需要在本地复制this.myVolatile来执行Java 8中的同步双if模式吗?,java,synchronization,volatile,Java,Synchronization,Volatile,更改现有代码是否有任何好处: class MyClass { volatile Object myVariable; Object myMethod() { if (myVariable == null) { synchronized(this) { if (myVariable == null) { myVariable = this.getNewValue();

更改现有代码是否有任何好处:

class MyClass {
    volatile Object myVariable;
    Object myMethod() {
        if (myVariable == null) {
            synchronized(this) {
                if (myVariable == null) {
                    myVariable = this.getNewValue();
                }
            }
        }
        return myVariable;
    }
}


我不认为在使用this.myVariable之前在本地复制它有什么意义,而且我认为对每个类变量使用“this.”不是一个好的做法。

复制到本地变量更有效、更正确

更有效:假设在常见情况下,
myVariable
为非空。在第一个版本中,您对
myVariable
执行两次读取,一次检查null,一次返回值。在第二个版本中,您一次读取
myVariable
,两次读取
tmp
(局部变量访问,这很简单)。使用volatile的关键在于强大的内存保证,这些保证意味着两次读取比一次读取对性能的影响非常大

更正确:假设
myVariable
是某种需要定期刷新的“缓存”。也就是说,有一个后台线程定期将
myVariable
设置为
null
,以便下次读取时重新加载。在第一个版本中,您对
myVariable
执行两次单独的读取。第一次读取可能返回非
null
,然后“缓存刷新”逻辑运行并将
myVariable
设置为
null
。第二次读取(返回值)然后返回
null
!在第二个版本中,
tmp
始终是为
null
测试的值(当然,假设
getNewValue()
从不返回
null

请注意,在本代码中使用“this.”是一种风格选择,与正确性或性能问题无关


这主要是对中所述内容的扩展。

void Object myMethod()
-我看到的是两种返回类型还是需要一杯咖啡?请参阅:注意局部变量“localRef”,它似乎不必要。这样做的效果是,在助手已经初始化的情况下(即大多数时间),volatile字段只被访问一次(由于“return localRef;”而不是“return helper;”@Kartik:您可能在编辑之间发现了我。现在检查一下。@Kartik:是的,你是对的,这没有意义。我不知道OP是什么意思。如果可以,请随意编辑。@JamesKPolk我们不应该编辑它,因为它会改变整个代码-查看如何从
if
块返回某些内容,但不会从外部返回任何内容。OP应该先编译代码。我认为它“可能更正确”,而不是“更正确”。这个习惯用法在很多情况下都会用到,
myVariable
永远不会被设置回null。@ruakh-fair,我在扩展这个观点。但是,我看到过类似的逻辑,在这种逻辑中,对volatile变量进行某些状态的测试,然后在return语句中再次使用volatile变量,从而打开了一个测试无法再保持为真的漏洞。在使用易失性变量时,将其复制到局部变量始终是一种良好的做法(不管程序员目前对如何操作它的看法如何)。
class MyClass {
    volatile Object myVariable;
    Object myMethod() {
        Object tmp = this.myVariable;
        if (tmp == null) {
            synchronized(this) {
                tmp = this.myVariable;
                if (tmp == null) {
                    this.myVariable = tmp = this.getNewValue();
                }
            }   
        }
        return tmp;
    }
}