6

这个问题在某种程度上是这个问题的延续和扩展,因为我认为这是一个完美的问题:分配给局部变量在这里有什么帮助?

这个问题基于Item 71of 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通过将字段的值分配给局部变量来访问字段? (以存档最佳性能)

即一些成语:

  1. 我们有一些volatile领域,就叫它吧volatileField

  2. 如果我们想在多线程方法中读取它的值,我们应该:

    1. 创建具有相同类型的局部变量:localVolatileVariable
    2. 分配 volatile 字段的值:localVolatileVariable = volatileField
    3. 从此本地副本中读取值,例如:

      if (localVolatileVariable != null) { ... }
      
4

3 回答 3

4

如果您计划执行任何类型的多步逻辑(当然假设该字段是可变的),则必须将 volatile 变量分配给本地字段。

例如:

volatile String _field;

public int getFieldLength() {
  String tmp = _field;
  if(tmp != null) {
    return tmp.length();
  }
  return 0;
}

如果您没有使用 的本地副本_field,则值可能会在“if”测试和“length()”方法调用之间发生变化,从而可能导致 NPE。

除了通过不进行超过一次易失性读取来提高速度的明显好处之外。

于 2013-04-19T11:06:48.957 回答
1

硬币有两个面。

一方面,对 volatile 的赋值就像一个内存屏障,JIT 不太可能使用 computeFieldValue 调用重新排序赋值。

另一方面,理论上这段代码会破坏 JMM。因为由于某些原因,某些JVM 被允许通过赋值重新排序 computeFieldValue 并且您会看到部分初始化的对象。只要变量读取不是变量写入的顺序,这是可能的。

field = result = computeFieldValue();  

之前没有发生

if (result == null) { // First check (no locking) 

只要 Java 代码应该是“一次编写,到处运行”,DCL 就是一种不好的做法,应该避免。此代码已损坏,不是考虑的重点。

如果您在一个方法中多次读取 volatile 变量,通过首先将其分配给局部变量,您可以最大限度地减少此类读取,这会更昂贵。但我不认为,你会得到性能提升。这可能是理论上的改进。这种优化应该留给 JIT,而不是开发人员考虑的重点。我同意这一点

于 2013-04-19T10:39:07.547 回答
0

他没有可能对 volatile 变量进行两次读取,而是只读取一次。读取 volatile 可能比通常的变量要慢一些。但即便如此,我们在这里谈论的是纳秒。

于 2013-04-19T12:59:21.503 回答