您的位置:首页 > 产品设计 > UI/UE

ArrayBlockingQueue源码中为什么方法要用局部变量引用类变量

2016-05-30 14:42 447 查看
问题:

类变量:

private final E[] items;

private final ReentrantLock lock;

方法:

public void put(E o) throws InterruptedException {

        if (o == null) throw new NullPointerException();

       final E[] items = this.items;

        final ReentrantLock lock = this.lock;


        lock.lockInterruptibly();

        try {

            try {

                while (count == items.length)

                    notFull.await();

            } catch (InterruptedException ie) {

                notFull.signal(); // propagate to non-interrupted thread

                throw ie;

            }

            insert(o);

        } finally {

            lock.unlock();

        }

    }

看黑体字部分 put方法中局部变量items和lock的使用有什么意义?

答案1:

首先在JDK 7中,这段代码变成这样子了:

Java代码  


final Object[] items;  
  
public void put(E e) throws InterruptedException {  
      checkNotNull(e);  
      final ReentrantLock lock = this.lock;  
      lock.lockInterruptibly();  
      try {  
          while (count == items.length)  
              notFull.await();  
          insert(e);  
      } finally {  
          lock.unlock();  
      }  
  }  

然后做个实验:

Java代码  


final Object[] items = new Object[10];  
  
public void test() {  
  if(items.length == 0) {  
  }  
   
  int i = items.length;   
}  
  
public void test2() {  
  final Object[] items = this.items;  
  if(items.length == 0) {  
  }  
   
  int i = items.length;  
}  

然后javap一下,javap -p -c -s Test >> Test.log,得到如下代码:

Java代码  


public void test();  
  Signature: ()V  
  Code:  
     0: aload_0         
     1: getfield      #3                  // Field items:[Ljava/lang/Object;  
     4: arraylength     
     5: ifne          8  
     8: aload_0         
     9: getfield      #3                  // Field items:[Ljava/lang/Object;  
    12: arraylength     
    13: istore_1        
    14: return          
  
public void test2();  
  Signature: ()V  
  Code:  
     0: aload_0         
     1: getfield      #3                  // Field items:[Ljava/lang/Object;  
     4: astore_1        
     5: aload_1                           //load 局部变量 items  
     6: arraylength     
     7: ifne          10  
    10: aload_1         
    // 这里少了getfield,因为aload_1 load的就是items  
    11: arraylength     
    12: istore_2        
    13: return    

两种写法唯一的区别,是getfield指令,getfield在对象内相对来说开销是比较廉价的,但前者(test方法)显然在代码可读性上,高出很多,如果不存在大量的实例变量引用,性能可以忽略不计,估计这也正是为什么JDK7采用这种简单的写法的原因吧。

答案2:

之前回答过一篇类似的:但不一样:
http://www.iteye.com/problems/87675

我的理解:

        final E[] items = this.items;

        final ReentrantLock lock = this.lock;

1、final的数据不可变,因此更安全,防止意外修改,阅读代码时更清晰(我们知道这个东西不能修改,更易于读代码),是一种好的编程习惯;

2、更快,因为你每次都直接this.items会发生如下操作(字节码表示):

    4:   aload_0  //0 表示当前对象

    5:   getfield        #171;  //得到当前对象的items 

   

    从字节码可以看出需要两条指令;

  

    如果 final E[] items = this.items;    如果接下来使用的话,直接从堆栈取items的引用,更快。

原文地址: http://www.iteye.com/problems/87918
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: