2

我有一堆从基类启动的 Runnable 对象。这些对象随机迭代并删除共享 ArrayList 中的项目。我已经同步了这两种方法,但得到了一个ConcurrentModicationException. 我相信这是因为它们是同步的,但彼此不同步。是这样吗?如果是这样,我应该获得哪个类的锁定?

环境类:

class Environment{

     ArrayList<Critter> critter_array = new ArrayList<Critter>();
     ArrayList<Food> food_array = new ArrayList<Food>();

     Environment(){
         Executor ex = Executors.newCachedThreadPool(); 
         Critter A = new Critter();
         Critter B = new Critter();
         critter_array.add(A);
         critter_array.add(B);
         ex.execute(A); 
         ex.execute(B);
     }

     public synchronized void destroyFood(Food item){
         Iterator<Food> iter = food_array.iterator();
         while(iter.hasNext()){
             Food temp = iter.next();
             food_array.remove(temp);
             break;
         }      
     }

}

小动物类:

class Critter implements Runnable{
     Environment envi;

     Critter(Environment E){
          envi = E;
     }

     @Override
     public void run() {
         //do other stuff
         CritterUtilities.iteratorMethod(this, envi);
     }
}

CritterUtilities 类:

class CritterUtilities{

    public static synchronized iteratorMethod(Critter self, Environment envi){
         Iterator<OtherObject> iter = envi.getFood().listIterator();
         while(iter.hasNext()){
             Food temp = iter.next();   /////////Problem is here
             //other stuff that's not related to the prob
        }
    }

}

堆栈跟踪:

Exception in thread "pool-1-thread-2" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at java.util.AbstractList$Itr.next(Unknown Source)
at CritterUtil.getViewableFood(CritterUtil.java:28)
at CritterUtil.getNearestFood(CritterUtil.java:41)
at Critter.hunt(Critter.java:163)
at Critter.run(Critter.java:139)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
4

2 回答 2

4

该错误不一定是多线程,而是您在使用迭代器时从列表中删除项目的事实:

 public synchronized void destroyFood(Food item){
     Iterator<Food> iter = food_array.iterator();
     while(iter.hasNext()){
         Food temp = iter.next();
         food_array.remove(temp); // <-- this is the problem
         break;
     }      
 }

相反,请使用以下Iterator.remove()方法:

 public synchronized void destroyFood(Food item){
     Iterator<Food> iter = food_array.iterator();
     while(iter.hasNext()){
         iter.remove(); // <-- this is how you remove elements while iterating
         break;
     }      
 }

另一方面,您的同步错误的。您的每个synchronized方法都在不同的对象上同步。为了让它们都在同一个对象上同步,最简单的方法可能是让它们都在列表本身上同步。

于 2013-08-05T15:58:01.530 回答
1

除了道文发现的删除错误外,同步也有问题。iteratorMethod()并且destroyFood()不要使用相同的对象进行同步。目前他们使用各自的对象进行同步。它应该是这样的:

 public void destroyFood(Food item) {
     synchronized (food_array) {
         // ... proceed with the removal
     }
 }

public static iteratorMethod(Critter self, Environment envi){
    List<OtherObject> list = envi.getFood();
    synchronized (list) {
        // the rest of iteratorMethod should go here
    }
}

(我假设 getFood() 返回食物数组,这样对象就会相同)。

相同的修复应该应用于修改或迭代食物列表的任何其他方法。

于 2013-08-05T16:21:14.860 回答