这个问题在某种程度上有所延续和扩展,因为我认为完美问题:How does assigning to a local variable help here?
此问题基于Item 71
Effective Java
,建议通过引入volatile
字段访问权限的本地变量来加快效果:
private volatile FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) { // First check (no locking)
synchronized(this) {
result = field;
if (result == null) // Second check (with locking)
field = result = computeFieldValue();
}
}
return result;
}
所以,我的问题更常见:
我们是否应始终通过将其值分配给本地变量来访问volatile
字段? (为了存档最佳效果)。
即。一些习语:
我们有一些volatile
字段,只调用volatileField
;
如果我们想在多线程方法中读取它的值,我们应该:
localVolatileVariable
localVolatileVariable = volatileField
从本地副本中读取值,例如:
if (localVolatileVariable != null) { ... }
答案 0 :(得分:3)
如果您计划执行任何类型的多步逻辑(当然,假设该字段是可变的),您必须将volatile变量分配给本地字段。
例如:
volatile String _field;
public int getFieldLength() {
String tmp = _field;
if(tmp != null) {
return tmp.length();
}
return 0;
}
如果您没有使用_field
的本地副本,则该值可能会在“if”测试和“length()”方法调用之间发生变化,从而可能导致NPE。
除了通过不进行多次易失性读取而提高速度的明显好处之外。
答案 1 :(得分:1)
硬币有两面。
一方面,对易失性工作的分配就像内存屏障一样,JIT不太可能使用computeFieldValue调用重新排序赋值。
另一方面,理论上这个代码打破了JMM。因为某些原因,某些 JVM可以通过赋值重新排序 computeFieldValue,并且您会看到部分初始化的对象。只要变量read不是带有变量write的顺序,这是可能的。
field = result = computeFieldValue();
在
之前不会发生if (result == null) { // First check (no locking)
只要java代码应该“一次写入”,DCL就是一种不好的做法,应该避免使用。此代码已被破坏,不是考虑因素。
如果在方法中对volatile变量进行多次读取,则首先将其分配给局部变量,然后将这些读取最小化,这样会更昂贵。但我不认为,你会获得性能提升。这可能是理论上的改进。这种优化应留给JIT,而不是开发人员的考虑因素。我同意this。
答案 2 :(得分:0)
而不是潜在地执行 TWO 读取volatile变量,他只做一个。 读取volatile可能比通常的变量慢一点。但即使如此,我们在这里谈论纳秒。