代码:
类变量:
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的使用有什么意义?
首先在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采用这种简单的写法的原因吧。
并不仅仅是 ArrayBlockingQueue ,还有 很多集合类,只要涉及到 set ,put 方法的 ,基本都是这样类似的 做法 , 我也最近在看concurrent 的源码, 也搞不太清具体是为什么, 但是 我觉得 这 极有可能 是跟 ,对象锁 与类 锁的 区别:
你看看 这个:
在java虚拟机中,每个对象和类在逻辑上都是和一个监视器相关联的。
对于对象来说,相关联的监视器保护对象的实例变量。
对于类来说,监视器保护类的类变量。
(如果一个对象没有实例变量,或者一个类没有变量,相关联的监视器就什么也不监视。)
为了实现监视器的排他性监视能力,java虚拟机为每一个对象和类都关联一个锁。代表任何时候只允许一个线程拥有的特权。线程访问实例变量或者类变量不需锁。
但是如果线程获取了锁,那么在它释放这个锁之前,就没有其他线程可以获取同样数据的锁了。(锁住一个对象就是获取对象相关联的监视器)
类锁实际上用对象锁来实现。当虚拟机装载一个class文件的时候,它就会创建一个java.lang.Class类的实例。当锁住一个对象的时候,实际上锁住的是那个类的Class对象。
一个线程可以多次对同一个对象上锁。对于每一个对象,java虚拟机维护一个加锁计数器,线程每获得一次该对象,计数器就加1,每释放一次,计数器就减 1,当计数器值为0时,锁就被完全释放了。
之前回答过一篇类似的:但不一样:
[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的引用,更快。