【发布时间】:2015-09-09 17:36:54
【问题描述】:
我有以下情况,
public class Test {
private static final int MAX_NUMBER = 10_00_00;
public static void main(String[] args) {
List<Integer> list = new CopyOnWriteArrayList<>();
long start = System.nanoTime();
for(int i = 0; i < MAX_NUMBER; i++) {
list.add(i * 2);
}
long end = System.nanoTime();
System.out.println(((end - start) / Math.pow(10, 9)));
}
}
输出
6.861539857
与 ArrayList 相比,它添加元素的速度非常缓慢,后者大约需要 0.004690843。我在文档中知道了原因,
ArrayList的线程安全变体,其中所有可变操作 (添加、设置等)是通过制作新的副本来实现的 底层数组。
所以,我的理解是,每当我在此列表中添加新元素时,它都会创建新的新数组并在此数组的最后一个索引处添加元素。我在add 方法中找到了一个锁,除此之外,该方法实际上每次都创建新数组。
当我将MAX_NUMBER 增加到10_00_000 时,我的程序会继续运行并且永远不会结束(它会,但我不能等待这么久)。
我认为Collections.synchronizedList 是您想要快速实现线程安全的更好选择。我用了它,大约花了0.007673728。
我的问题:
- 为什么在内部创建新数组线程安全与此有关?
- 为什么在
MAX_NUMBER = 10_00_000的情况下花了这么多时间? (因为MAX_NUMBER = 10_00_00花了大约 6 秒)这是因为变异操作每次都会创建新数组吗? - 这是否意味着
CopyOnWriteArrayList在您拥有大量元素并且最好选择其他元素(即Collections.synchronizedList)时存在性能缺陷? - 这就是我们通常在公共 API 中看不到
CopyOnWriteArrayList的原因吗?除此之外还有什么缺点吗?
【问题讨论】:
-
Javadoc: 这通常成本太高,但当遍历操作的数量远远超过突变时,它可能比替代方案更有效,并且当您不能或不想同步遍历但需要同步遍历时很有用排除并发线程之间的干扰
标签: java performance thread-safety copyonwritearraylist