0

我有一个在多线程环境中使用的并发列表。一旦建立了列表,大部分操作就是遍历它。我想知道以下两种方法中哪一种更有效,或者创建新列表与使用同步的成本是多少?或者也许还有其他更好的方法?

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;
    }
}

UPDATE getAllValue1():它是线程安全的,因为它获取 CopyOnWriteList 的快照,而 CopyOnWriteList 本身就是线程安全列表。但是,正如 sharakan 指出的那样,成本是迭代 2 个列表,并创建一个本地对象 ArrayList,如果原始列表很大,这可能会很昂贵。

getAllValue2():在同步块中也是线程安全的。(假设其他函数正确同步。)将它放在同步块中的原因是因为我想预先分配数组,以确保 .size() 调用与迭代同步。(迭代部分是线程安全的,因为它使用 CopyOnWriteList。)但是这里的成本是使用同步块的机会成本。如果有 100 万个客户端调用 getAllValue2(),每个客户端都必须等待。

所以我想答案真的取决于有多少并发用户需要读取数据。如果并发用户不多,可能方法2更好。否则,方法1更好。同意?

在我的使用中,我有几个并发客户端,可能首选方法 2。(顺便说一句,我的清单大约是 10k 大小)。

4

3 回答 3

0

编辑(再次):由于您在写入数组列表中使用了副本(应该更加细心),因此我将获取迭代器并使用它来初始化您的数组。由于迭代器是您请求时数组的快照,因此您可以在列表上同步以获取大小,然后进行迭代,而不必担心ConcurrentModificationException或让迭代器更改。

public int[] getAllValue1() {
    synchronized(list){
        int[] data = new int[list.size()];            
    }
    Iterator i = list.iterator();
    while(i.hasNext()){
        data[i++] = i.next().getValue();
    }
    return data;
}
于 2013-02-26T14:15:56.353 回答
0

getAllValue1 看起来不错,因为您需要根据对象的 aa 字段返回原始类型数组。这将是两次迭代,但一致,您不会在阅读器线程之间引起任何争用。您尚未发布任何分析结果,但除非您的列表很大,否则我更担心多线程环境中的争用,而不是两次完整迭代的成本。

如果您更改 API,则可以删除一次迭代。最简单的方法是返回一个 Collection ,如下所示:

public Collection<Integer> getAllValue1() {
    List<Integer> list2 = new ArrayList<Integer>(list.size());
    for (Object obj : list2) {
        list2.add(obj.getValue());
    }
    return list2;
}

如果您可以通过这种方式更改您的 API,那将是一种改进。

于 2013-02-26T14:23:44.717 回答
0

我觉得第二种效率更高。原因是,在第一个中,您创建另一个列表作为本地创建。这意味着如果原始列表包含大量数据,它将复制所有数据。如果它包含数百万个数据,那么这将是一个问题。

不过有list.toArray()方法

Collections 界面还包含一些有用的东西

  Collection synchronizedCollection = Collections.synchronizedCollection(list);

或者

List synchronizedList = Collections.synchronizedList(list);

如果您需要对象 VALUE,而不是对象,请使用您的第二个代码。否则,您可以用上述函数替换第二个代码的适当部分,然后做任何您想做的事情。

于 2013-02-26T14:23:49.247 回答