类中字段赋值给局部变量后再使用意义何在?

Concurrency-interest邮件列表中有人问了这么一个问题:ArrayBlockingQueue中有个对象字段lock,在ArrayBlockingQueue的很多方法中,使用这个lock时都将其先赋值给一个局部变量,然后再通过局部变量调用lock上的方法,而没有直接使用lock字段,如remainingCapacity方法中先将this.lock赋值给一个局部变量lock,然后再使用这个局部变量:
[code lang=”java”]
public class ArrayBlockingQueue {
private final ReentrantLock lock;

//…other fields and methods

public int remainingCapacity() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return items.length – count;
} finally {
lock.unlock();
}
}
}
[/code]
而不是像这样直接使用类中的字段:
[code lang=”java”]
public class ArrayBlockingQueue {
private final ReentrantLock lock;

//…other fields and methods

public int remainingCapacity() {
this.lock.lock();
try {
return items.length – count;
} finally {
this.lock.unlock();
}
}
}
[/code]
那么为什么要这么做,有什么理由或说法?

Doug Lea给出了回复,大意如下:

归根究底是由于内存模型与OOP之间的原则不一致。

几乎j.u.c包中的每个方法都采用了这样一种策略:当一个值会被多次使用时,就将这个字段读出来赋值给局部变量。虽然这种做法不雅观,但检查起来会更直观。

final字段也会做这样处理,可能有些令人不解。这是因为JVM并不足够智能,不能充分利用JMM已经提供了安全保证的可优化点,比如可以不用重新加载final值到缓存。相比过去,JVM在这方面有很大进步,但仍不够智能。

原文如下:

It’s ultimately due to the fundamental mismatch between memory models
and OOP 🙂

Just about every method in all of j.u.c adopts the policy of reading fields as locals whenever a value is used more than once.This way you are sure which value applies when.This is not often pretty, but is easier to visually verify.

The surprising case is doing this even for “final” fields.This is because JVMs are not always smart enough to exploit the fine points of the JMM and not reload read final values, as they would otherwise need to do across the volatile accesses entailed in locking. Some JVMs are smarter than they used to be about this, but still not always smart enough.

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 类中字段赋值给局部变量后再使用意义何在?

  • Trackback 关闭
  • 评论 (11)
  1. 不懂。

    • 主要是说方法中使用到对象的字段时,为什么要先赋值给局部变量这么多此一举。

    • Jack
    • 2013/04/12 2:42下午

    不明白~作者可以再详细解释一下吗

    • 已经更新,现在应该容易明白些了

  2. 只知道了jvm不够智能,虽然平时开发时也会这么做,但还是不知道为什么

    • 宅男小何
    • 2013/04/15 3:06下午

    这是一个优化?要是不赋值给一个局部变量对程序有何影响啊?
    求解答

    • 匿名
    • 2013/04/17 9:15上午

    “as they would otherwise need to do across the volatile accesses entailed in locking” 这一句怎么遗漏了呢。。。

    • ransom
    • 2013/04/27 8:57上午

    说说我的理解:
    并发编程存在缓冲行刷新的问题。这个在网站中很多文章都可以看到。
    这里把lock的变量重新定义成一个局部变量,是不是就相当于将其直接加入到了CPU的缓存里面去,
    这样,在其他处于同一个缓冲行的数据强制刷新的时候,就不会导致本次定义的局部变量再次刷新。
    这样就不会影响到该并发线程的执行。避免了由于缓冲行更新导致的并发停顿。

      • bobo
      • 2014/05/04 3:58下午

      是这样吗?这样做能避免这个局部变量不在同一个缓存行里?

    • 忧伤的木头
    • 2013/07/27 2:41下午

    ransom :
    说说我的理解:
    并发编程存在缓冲行刷新的问题。这个在网站中很多文章都可以看到。
    这里把lock的变量重新定义成一个局部变量,是不是就相当于将其直接加入到了CPU的缓存里面去,
    这样,在其他处于同一个缓冲行的数据强制刷新的时候,就不会导致本次定义的局部变量再次刷新。
    这样就不会影响到该并发线程的执行。避免了由于缓冲行更新导致的并发停顿。

    是这个意思吗?不解

    • cherin
    • 2014/08/02 10:35下午

    我觉得是是内存模型中堆栈寻址切换,局部变量将把对象引用自存放stack frame的local var区,无需通过this到堆中寻址。

return top