【发布时间】:2013-02-26 14:09:45
【问题描述】:
我有一个在多线程环境中使用的并发列表。一旦建立了列表,大部分操作就是遍历它。我想知道以下两种方法中哪一种更有效,或者创建新列表与使用同步的成本是多少?或者也许还有其他更好的方法?
List<Object> list = new CopyOnWriteArrayList<Object>();
public int[] getAllValue1() {
List<Object> list2 = new ArrayList<Object>(list);
int[] data = new int[list2.size()];
int i = 0;
for (Object obj : list2) {
data[i++] = obj.getValue();
}
return data;
}
public int[] getAllValue2() {
synchronized (list) {
int[] data = new int[list.size()];
int i = 0;
for (Object obj : list) {
data[i++] = obj.getValue();
}
return data;
}
}
更新 getAllValue1():它是线程安全的,因为它拍摄了 CopyOnWriteList 的快照,而 CopyOnWriteList 本身就是线程安全的 List。然而,正如 sharakan 指出的那样,成本是迭代 2 个列表,并创建一个本地对象 ArrayList,如果原始列表很大,这可能会很昂贵。
getAllValue2():在同步块中也是线程安全的。 (假设其他函数正确同步。)将它放在同步块中的原因是因为我想预先分配数组,以确保 .size() 调用与迭代同步。 (迭代部分是线程安全的,因为它使用 CopyOnWriteList。)但是这里的成本是使用同步块的机会成本。如果有 100 万个客户端调用 getAllValue2(),每个客户端都必须等待。
所以我想答案真的取决于有多少并发用户需要读取数据。如果并发用户不多,可能方法2更好。否则,方法1更好。同意吗?
在我的使用中,我有几个并发客户端,可能首选方法 2。 (顺便说一句,我的列表大小约为 10k)。
【问题讨论】:
-
但是有人写在名单上吗?此外,Object 没有 getValue() 方法。这不是您实际运行的代码。给我们更多:)
-
如何填充列表?填充列表的代码是否同步?
-
我跳过了初始化部分。一旦 List 被初始化,它就会留在内存中。有时,列表中会增加一些内容。
-
对象是大值类,包含很多字段,就像数据库中一张很宽的表。对于某些用户,他们希望从表中获取整数列,这就是我在这里尝试做的。由于它必须是线程安全的,所以我使用了 CopyOnWriteList。但是在遍历列表并返回 int[] 时,我仍然需要付出额外的努力使其成为线程安全的。我认为这两个函数都是线程安全遍历,如果我在这里错了,请纠正我。
-
@gd1 我相信您所指的 getAllValue1() 中的行会以线程安全的方式始终如一地发生,因为他使用的是
CopyOnWriteArrayList
标签: java performance synchronization