使用ExecutorService时出现java.util.ConcurrentModificationException异常

4

我正在开发一个项目,使用观察者设计模式。主题类是:

public class Subject {
    private List<Observer> observers = new ArrayList<Observer>();
    private int state;
    public void setState(int state) {
          this.state = state;
          notifyAllObservers();
    }
    public void attach(Observer observer){
          observers.add(observer);      
    }
    public void notifyAllObservers(){
          for (Observer observer : observers) {
             observer.update();
           }
   }
   public void deattach(Observer observer) {
         observers.remove(observer);
   }
}

观察者接口是:

public abstract class Observer implements Runnable{
    protected Subject subject;
    public abstract void update();
    public abstract void process();
}

其中一个被命名为HexObserver的观察者是:

public class HexaObserver extends Observer {

    private ExecutorService threadpool = Executors.newFixedThreadPool(10);

    public HexaObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }

    @Override
    public void update() {
        System.out.println("Hex String: "
                + Integer.toHexString(subject.getState()));
        Future future = threadpool.submit(new HexaObserver(subject));

    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        System.out.println("In run :D :D :D");

    }

    @Override
        public void process() {
            // TODO 
        }
}

需要测试的类是:

public class Demo {
    public static void main(String[] args) {
        Subject subject = new Subject();
        HexaObserver hob = new HexaObserver(subject);
        System.out.println("First state change: 15");
        subject.setState(15);
    }
}

当我尝试运行这个程序时,它报错了:
First state change: 15
Hex String: f
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
    at java.util.ArrayList$Itr.next(ArrayList.java:831)
    at Observer.Subject.notifyAllObservers(Subject.java:23)
    at Observer.Subject.setState(Subject.java:15)
    at Observer.Demo.main(Demo.java:12)
In run :D :D :D

我不明白为什么会出现这个错误,因为当我们尝试在不允许的情况下同时修改某个对象时,ConcurrentModificationException就会被抛出。

我是否漏掉了什么?


2
看起来这是因为你的HexaObserver在通知循环发生时向数组列表添加了一个元素。尝试将ArrayList更改为并发实现,例如CopyOnWriteArrayList - BretC
BretC 似乎是正确的。我不明白为什么在 update() 中添加了一个新的 HexaObserver(包括构造函数中的隐式附加)。可能意图使用 submit(this) 吗?或者提交一个新的不可变且线程安全的对象,而无需隐式附加。 - Markus Kull
@Bret C :嗯,[CopyOnWriterArrayList]会起作用,但代价很高。顺便说一句,Markus Kull建议的方法也不错。我真的只是想这样做。 - Geek_To_Learn
2个回答

1
两件事情同时发生:你正在迭代observers并向observers添加一个元素。这导致了ConcurrentModificationException
通常至少有三种方法可以解决:
  • 使用同步集合
  • 线程安全地复制集合并在副本上进行迭代
  • 手动使用synchronized块同步对observers的所有访问:

 

public void attach(Observer observer){
      synchronized(observers){ 
          observers.add(observer);      
      }
}
public void notifyAllObservers(){
      synchronized(observers){ 
          for (Observer observer : observers) {
             observer.update();
          }
      }
}
public void deattach(Observer observer) {
      synchronized(observers){ 
           observers.remove(observer);
      }
}
  • 您也可以将整个方法标记为synchronized,但这样它们将在Subject实例上同步,而不是在集合实例上。

然而,在您的情况下,问题与您的update()有关。
您确定您的HexaObserverupdate应该创建一个新的HexaObserver吗?因为您正在向已经包含该实例的集合中添加同一类的新实例。


是的,这是因为我在答案中提到的 new HexaObserver。您正在从同一线程迭代和修改相同的集合。 - Dariusz

0

ConcurrentModificationException通常是使用IteratorCollection上进行迭代时的标志,而在迭代过程中,您还修改了底层的Collection(请记住,foreach表达式实际上是使用Iterator的快捷方式)。唯一解决此问题的方法是对原始集合的副本进行迭代。如果您处于多线程环境中,还需要确保以线程安全的方式复制集合。

例如,您可以这样做:

public class Subject {
    private List<Observer> observers = new Vector<Observer>();
    private int state;
    public void setState(int state) {
          this.state = state;
          notifyAllObservers();
    }
    public void attach(Observer observer){
          observers.add(observer);      
    }
    public void notifyAllObservers(){
          for (Observer observer : ((List<Observer)observers.clone())) {
             observer.update();
           }
   }
   public void deattach(Observer observer) {
         observers.remove(observer);
   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接