65

我有一个预先填充的数组列表。而且我有多个线程将从数组列表中删除元素。每个线程调用下面的 remove 方法并从列表中删除一项。以下代码是否给了我一致的行为?

ArrayList<String> list = Collections.synchronizedList(new ArrayList<String>());

void remove(String item)
{
     do something; (doesn't work on the list)
     list.remove(item);
}

谢谢!

4

7 回答 7

71

是的,如果您也在迭代列表,请小心,因为在这种情况下,您将需要对其进行同步。从Javadoc

用户在迭代返回的列表时必须手动同步它:

List list = Collections.synchronizedList(new ArrayList());
    ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

或者,您可以使用CopyOnWriteArrayList哪个写入速度较慢但没有此问题。

于 2012-07-06T10:32:46.953 回答
24

只要您不需要“删除”方法是原子的,那应该没问题。

换句话说,例如,如果“做某事”检查该项目在列表中出现多次,那么当您到达下一行时,该检查的结果可能会出错。

此外,请确保在迭代时同步列表:

synchronized(list) {
    for (Object o : list) {}
}

正如 Peter Lawrey 所提到的,CopyOnWriteArrayList可以让您的生活更轻松,并且可以在高度并发的环境中提供更好的性能。

于 2012-07-06T10:32:08.970 回答
12

来自Collections#synchronizedList(List)javadoc

返回由指定列表支持的同步(线程安全)列表。为了保证串行访问, 对备份列表的所有访问都通过返回列表完成是至关重要的......当迭代返回列表时,用户必须手动同步返回列表。不遵循此建议可能会导致不确定的行为。

于 2012-07-06T10:34:57.893 回答
3

列表可能有 2 个不同的问题:
1) 如果您在迭代中进行修改,即使在单线程环境中,您也会遇到 ConcurrentModificationException,如下例所示:

List<String> list = new ArrayList<String>();
for (int i=0;i<5;i++)
   list.add("Hello "+i);

for(String msg:list)
   list.remove(msg);

所以,为了避免这个问题,你可以这样做:

for(int i=list.size()-1;i>=0;i--)
   list.remove(i);

2)第二个问题可能是多线程环境。如上所述,您可以使用 synchronized(list) 来避免异常。

于 2012-10-31T20:59:29.037 回答
0

Yes, it will work fine as you have synchronized the list . I would suggest you to use CopyOnWriteArrayList.

CopyOnWriteArrayList<String> cpList=new CopyOnWriteArrayList<String>(new ArrayList<String>());

    void remove(String item)
    {
         do something; (doesn't work on the list)
                 cpList..remove(item);
    }
于 2012-07-06T10:45:14.243 回答
0

它将为添加/删除操作提供一致的行为。但是在迭代时,您必须显式同步。参考这个链接

于 2012-07-06T10:37:42.880 回答
-6
synchronized(list) {
    for (Object o : list) {}
}
于 2014-05-22T07:50:32.807 回答