不要再卡了
2012-09-03 09:09
浏览 201
已采纳

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

代码:
类变量:
private final E[] items;
private final ReentrantLock lock;
方法:
public void put(E o) throws InterruptedException {
if (o == null) throw new NullPointerException();
[b] final E[] items = this.items;
final ReentrantLock lock = this.lock;[/b]
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的使用有什么意义?

  • 写回答
  • 好问题 提建议
  • 关注问题
  • 收藏
  • 邀请回答

3条回答 默认 最新

  • xpjsky 2012-09-04 00:37
    已采纳

    首先在JDK 7中,这段代码变成这样子了:
    [code="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();
    }
    }
    [/code]
    然后做个实验:
    [code="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;
    }
    [/code]
    然后javap一下,javap -p -c -s Test >> Test.log,得到如下代码:
    [code="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

    [/code]

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

    已采纳该答案
    评论
    解决 无用
    打赏 举报
  • navy0168 2012-09-03 09:18

    并不仅仅是 ArrayBlockingQueue ,还有 很多集合类,只要涉及到 set ,put 方法的 ,基本都是这样类似的 做法 , 我也最近在看concurrent 的源码, 也搞不太清具体是为什么, 但是 我觉得 这 极有可能 是跟 ,对象锁 与类 锁的 区别:

    你看看 这个:

    在java虚拟机中,每个对象和类在逻辑上都是和一个监视器相关联的。
    对于对象来说,相关联的监视器保护对象的实例变量。

    对于类来说,监视器保护类的类变量。

    (如果一个对象没有实例变量,或者一个类没有变量,相关联的监视器就什么也不监视。)
    为了实现监视器的排他性监视能力,java虚拟机为每一个对象和类都关联一个锁。代表任何时候只允许一个线程拥有的特权。线程访问实例变量或者类变量不需锁。

    但是如果线程获取了锁,那么在它释放这个锁之前,就没有其他线程可以获取同样数据的锁了。(锁住一个对象就是获取对象相关联的监视器)

    类锁实际上用对象锁来实现。当虚拟机装载一个class文件的时候,它就会创建一个java.lang.Class类的实例。当锁住一个对象的时候,实际上锁住的是那个类的Class对象。

    一个线程可以多次对同一个对象上锁。对于每一个对象,java虚拟机维护一个加锁计数器,线程每获得一次该对象,计数器就加1,每释放一次,计数器就减 1,当计数器值为0时,锁就被完全释放了。

    评论
    解决 无用
    打赏 举报
  • jinnianshilongnian 2012-09-03 13:22

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

    我的理解:
    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的引用,更快。
    
    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题