关于ConcurrentModificationException
对象的ArrayList
有很多问题,但我找不到问题的答案。
在我的servlet
中,我有一个ArrayList作为成员对象:
List myList<Object> = new ArrayList<Object> (...);
列表必须在用户和会话之间共享。
在servlet的一个方法method1
中,我需要迭代ArrayList
项,最后在迭代后添加清单。这是一个片段:
for (Object o : myList) {
// read item o
}
myList.clear();
在另一种方法method2
中,我只需在列表中添加一个新项目。
大多数情况下,该方法无错误地结束其工作。有时,可能是由于不同用户同时调用此方法,我得到了着名的java util.ConcurrentModificationException
异常。
我应该将我的列表定义为:
List myList = Collections.synchronizedList(new ArrayList(...));
这是否足够或我错过了什么?幕后是什么?当存在可能的并发时,第二个线程是否由容器保持待机状态?
编辑:我已经添加了一些评论的答案。答案 0 :(得分:4)
使用同步列表无法解决您的问题。问题的核心是您正在迭代列表并同时修改它。您需要使用互斥机制(同步块,锁等)以确保它们不会同时发生。详细说明一下,如果你从:
开始methodA() {
iterate over list {
}
edit list;
}
methodB() {
edit list;
}
如果您使用同步列表,那么您实际得到的是:
methodA() {
iterate over list {
}
synchronized {
edit list;
}
}
methodB() {
synchronized {
edit list;
}
}
但你真正想要的是:
methodA() {
synchronized {
iterate over list {
}
edit list;
}
}
methodB() {
synchronized {
edit list;
}
}
答案 1 :(得分:2)
只使用synchronizedList使所有方法都是线程安全的,除了Iterators。
我会使用CopyOnWriteArrayList
。它是线程安全的,不会产生ConcurrentModificationException。
答案 2 :(得分:1)
ConcurrentModificaitonException
。我想,当你执行一些条件操作时,只会抛出错误。
我建议您将要添加/删除的值推送到单独的列表中,并在完成迭代后执行添加/删除。
答案 3 :(得分:1)
您不仅要锁定方法访问,还要锁定对列表的使用。
因此,如果你分配一个配对的对象,如:
Object myList_LOCK = new Object();
然后您可以在访问List时锁定该对象,如下所示:
synchronized(myList_LOCK) {
//Iterate through list AND modify all within the same lock
}
目前你所做的唯一锁定是在List的各个方法中,这在你的情况下是不够的,因为你需要整个迭代和修改序列的原子性。
您可以使用实际对象(myList)来锁定而不是配对对象,但根据我的经验,您最好使用另一个专用对象,因为它可以避免由于对象内部的代码而导致的意外死锁条件锁定对象本身。
答案 4 :(得分:1)
这是Peter Lawery的回答。但是,由于复制不会对你产生负面影响,你可以混合使用同步进行复制。
private final List<Object> myList = new ArrayList<Object>();
public void iterateAndClear(){
List<Object> local = null;
synchronized(myList){
local = new ArrayList<Object>(myList);
myList.clear();
}
for(Object o : local){
//read o
}
}
public void add(Object o){
synchronized(myList){
myList.add(o);
}
}
在这里,您可以迭代o
个元素而不用担心编纂(在任何类型的同步之外),同时myList
被安全地清除并添加到。{/ p>