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;
}
}
在这种情况下,我们有三种解读:
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;
}
}
在这种情况下,我们有三种解读:
helperrapper
已初始化
添加局部变量解决了这个问题,因为我们将helperWrapper
值分配给tempWrapper
,然后以什么顺序读取tempWrapper
并不重要。如果它有一个非null值,它将用于null检查和return语句
之所以会发生这种情况,是因为Java内存模型允许仅出于优化目的对操作进行重新排序。请看以下引用:
什么是重新排序?
在许多情况下,访问程序变量
(对象实例字段、类静态字段和数组元素)可以
以与指定的不同的顺序执行
节目。编译器可以自由地对命令进行排序
以优化为名的指令。处理器可以执行
在某些情况下,指示不正常。数据可能是
在内存中的寄存器、处理器缓存和主内存之间移动
与程序指定的顺序不同
[……]
编译器、运行时和硬件应该共同创建
似乎是串行语义的错觉,这意味着
单线程程序,程序应该不能观察到
重新排序的影响。H