我的课程中有以下代码:
private static LinkedList<MyObject> myList =
new LinkedList<MyObject>();
public static void doEventStuff(String user, String event){
LinkedList<MyObject> copy;
synchronized (myList) {
copy = new LinkedList<>(myList);
}
for (MyObject o : copy) {
... do something with objects o
}
}
public static void removeObject(MyObject o) {
synchronized (myList) {
myList.remove(o);
}
o.doCleanup();
}
public static void terminate() {
synchronized (myList) {
for (MyObject o : myList) {
o.doCleanup();
}
myList.clear();
}
}
public static List<MyObject> getMyObjectsCopy() {
synchronized (myList) {
return new LinkedList<>(myList);
}
}
我的问题是调用terminate()时出现ConcurrentModificationException,特别是在迭代“for(MyObject o:myList)”时。
列表myList不会传递,只能通过静态方法访问。 另外:方法MyObject.doCleanup()ca触发事件,其中可以调用方法“removeObject(MyObject)”,当在finally()mthod内部进行迭代时,但由于所有方法都同步 在“myList”上,我不相信会发生并发异常。
任何人都可以帮我解决这个问题吗?
答案 0 :(得分:2)
ConcurrentModificationException
。 synchronize
将帮助避免其他线程访问您的列表,但您的问题不是由于线程并发。如果要在迭代列表时删除(从同一个线程),则必须使用iterator
并调用iterator.remove()
。
答案 1 :(得分:2)
这本身不是multi-threading
问题,如果您从foreach
循环中的列表中删除对象,您将获得ConcurrentModificationException
。
顺便说一下,您可以使用CopyOnWriteArrayList代替
答案 2 :(得分:0)
在此代码中:
for (MyObject o : myList) {
o.doCleanup(o);
}
您调用代码,该代码在内部调用removeObject()方法。在这个调用中,我们创建myList.remove(o),它将更改一个列表,结果就像:
for (MyObject o : myList) {
myList.remove();
}
所以,这不是一个并发问题,只是在这个集合的forEach循环中修改集合。我认为这种情况的最佳解决方案是避免在doCleanup()代码中从myList中删除,看起来缺乏设计。 其他可能的解决方案 - 另一个doCleanup()方法版本,它不会抛出导致从集合中删除的事件 - 您已经执行了myList.clear()。 或者重写removeObject()方法,如:
public static void removeObject(MyObject o) {
synchronized (myList) {
for (Iterator<MyObject> it = myList.iterator(); it.hasNext(); ) {
MyObject o1 = it.next();
if (o1.equals(o)) {
it.remove();
}
}
}
o.doCleanup();
}
就像我理解的那样,@ geert3在他的回答中建议,但这个答案的动机并不完全清楚。
但是我不喜欢上一个解决方案 - 它看起来像是一个设计问题的黑客,因为在这个全局集合中维护代码我们在已删除的对象上调用doCleanup(),它应该在事件处理程序中再调用一个removeObject() - 我认为它最好删除这个“递归”。