Java 对象的易失性和安全发布
在少数文章中,它说volatile的使用修复了双重检查锁定问题Java 对象的易失性和安全发布,java,multithreading,Java,Multithreading,在少数文章中,它说volatile的使用修复了双重检查锁定问题 class Foo { private volatile Helper helper = null; public Helper getHelper() { if (helper == null) { synchronized(this) { if (helper == null)
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null)
helper = new Helper(); //Important
}
}
return helper;
}
}
但在这里,即使我们使用volatile for helper字段,这怎么可能是一个安全的发布呢?
我的意思是,这怎么能保证我们不会得到一致性帮助对象呢?我认为这是因为JVM保留了
Helper=new Helper()
的顺序。我的意思是,只有在创建对象之后,分配才会发生。如果我错了,请纠正我。我认为这是因为JVM保留了helper=new helper()
的顺序。我的意思是,只有在创建对象之后,分配才会发生。如果我错了,请纠正我。好的,同步部分之外的检查是为了避免不必要地进入监视器部分。如果您正在尝试优化性能,则可以省略该选项。只有在创建对象后无法将helper重置为null时,它才起作用。同步部分中的检查是为了避免不一致(因为在我们等待进入监视器时可能已经创建了对象)
synchronized的内部部分只能以互斥方式执行,这就是一致性的来源。但是,同样:如果(helper==null)外部
仅在helper在被分配了对helper实例的新引用后不能再变为null时才被允许。好的,同步部分之外的检查是为了避免不必要地进入监视部分。如果您正在尝试优化性能,则可以省略该选项。只有在创建对象后无法将helper重置为null时,它才起作用。同步部分中的检查是为了避免不一致(因为在我们等待进入监视器时可能已经创建了对象)
synchronized的内部部分只能以互斥方式执行,这就是一致性的来源。但是,同样:外部if(helper==null)
仅当helper在被分配了对helper实例的新引用后不能再变为null时才允许使用。从高级角度来看,volatile
保证了volatile写入之前发生的任何操作的结果(即,new Helper()
)中的操作将在后续volatile读取后对任何其他线程可见(即,当另一个线程在外部检查中看到Helper!=null
)
从低级角度来看,它取决于实现。但一般来说:
- 应禁止使用以前的操作对易失性写入进行重新排序的编译器优化
- JIT应该插入到生成的代码中,以防止在CPU级别进行类似的重新排序(如果特定的CPU体系结构允许这样的重新排序)
从高层角度来看,volatile
保证在volatile写入之前发生的任何操作的结果(即new Helper()
内的操作)在后续volatile读取之后(即,当另一个线程在外部检查中看到Helper!=null
时)将对任何其他线程可见
从低级角度来看,它取决于实现。但一般来说:
- 应禁止使用以前的操作对易失性写入进行重新排序的编译器优化
- JIT应该插入到生成的代码中,以防止在CPU级别进行类似的重新排序(如果特定的CPU体系结构允许这样的重新排序)
没有volatile,因为如果(helper==null)
的初始在同步块之外,则不存在可见性/一致性保证。特别是,helper
可能不是null,而是指仅部分创建的对象
这是因为helper=newhelper()
不是原子操作:
它分配一些内存
它构造对象
它将对象的引用指定给helper
如果没有同步,观察线程可以以任何顺序看到这些操作,特别是它可以看到2之前的3
通过将helper
设置为volatile,可以在写入和读取之间引入“发生在之前”关系(定义为),这确保如果从观察线程中看到3,也会看到1和2
特别是,在volatile写入之前执行的任何操作都将从volatile读取中可见(当然是后续操作)。没有volatile,因为初始if(helper==null)
在同步块之外,没有可见性/一致性保证。特别是,helper
可能不为null,但引用了仅部分创建的对象
这是因为helper=newhelper()
不是原子操作:
它分配一些内存
它构造对象
它将对象的引用指定给helper
如果没有同步,观察线程可以以任何顺序看到这些操作,特别是它可以看到2之前的3
通过将helper
设置为volatile,可以在写入和读取之间引入“发生在之前”关系(定义为),这确保如果从观察线程中看到3,也会看到1和2
特别是,在易失性写入之前执行的任何操作都将从易失性读取中可见(当然,如果是后续操作).您的意思是,在volatile write之前涉及volatile字段的任何操作对后续volatile read都是可见的吗?所有操作。在同一线程上的所有早期语句和write中都有一个before,在读取线程上的read语句和所有subqent语句之间也有一个before。因此,它的作用类似于m埃默里障碍影响