素材牛VIP会员
关于CopyOnWriteArrayList源码的疑惑
 捂***功  分类:Java代码  人气:619  回帖:3  发布于6年前 收藏

jdk concurrent包当中的CopyOnWriteArrayList定义了一个数组和一个锁:
private volatile transient Object[] array;
transient final ReentrantLock lock = new ReentrantLock();

他的add方法如下:
public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } }

问题:
add方法中的final ReentrantLock lock = this.lock;这一句是不是必须的?为什么又定义了一个锁,并且实质用的还是全局定义的那个锁。如果直接用this.lock可不可以?
如果不用getArray直接写成
int len = array.length; Object[] newElements = Arrays.copyOf(array, len + 1); newElements[len] = o; array = newElements;
是不是也可以?

 标签:java

讨论这个帖子(3)垃圾回帖将一律封号处理……

Lv2 入门
th***as 职业无 6年前#1

问题已赞.

应该还有性能上的考虑. 在java函数栈帧的本地变量里存入对 域成员对象的引用, 相当于一个空间换时间的做法. 毕竟 取本地变量 只需要 aload_x 一条指令; 而取 域成员对象的话, 需要 aload_0 [this], getfield 两条指令. 且每次从 栈里取值, 应该还有缓存上的 好处.

Lv3 码奴
上***水 职业无 6年前#2

好久不用java了。是不是为了防止其它线程修改 lock 的值啊?

Lv1 新人
真***溜 职业无 6年前#3

是的。用局部变量 lock 来引用自身的成员变量当然不是必须的。我认为这是编程演进过程所引入的,很可能和内部执行的规范有关。

例如:内部规范上可能是要求尽量使用 accessor 来使用成员变量,避免直接使用。即使需要直接使用,最好先用局部变量来引用它。这样做的好处是,这个函数自身具有很好的完备性。设想如果此类如果修改,将 lock 成员变量取消或者更改类型,如果直接在代码各处引用 lock,势必造成很多修改。而在目前的实现上,不过是将
final ReentrantLock lock = this.lock; 改成 final ReentrantLock lock = getLock(); 影响小得多,语义十分清晰。

同样,对成员变量 array 的引用也是这样,通过使用 accessor getArray(),代码可以将对成员变量的依赖降低到对一个成员函数的依赖,这对维护也非常有好处。

感谢题主引用了很好的一段程序,提出了一个非常完整的问题。

 文明上网,理性发言!   😉 阿里云幸运券,戳我领取