2015-05-06 3 views
2

Я работаю над некоторым проектом, в котором я использую шаблон проектирования Observer. Тема класс:java.util.ConcurrentModificationException при использовании ExecutorService

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

+0

BretC кажется правильным. Я не понимаю, почему в update() добавлен новый HexaObserver (включая неявное прикрепление в Constructor). Вероятно, 'submit (this)' был предназначен? Или подайте новый неизменный и потокобезопасный объект без неявного прикрепления. –

+0

@Bret C: yaa [CopyOnWriterArrayList] будет работать, но это так дорого. Кстати, метод, предложенный Маркусом Куллом, тоже хорош. Я действительно намеревался это только. –

ответ

0

Две вещи происходят одновременно: вы выполняете итерацию свыше observers и добавляете элемент к observers. Это вызывает ваш ConcurrentModificationException

В общем есть две вещи, которые вы можете сделать:

  • использовать синхронизированную коллекцию
  • потоков безопасно скопировать коллекцию и перебирать на копии
  • вручную синхронизировать все доступ к observers с synchronized блока:

 

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? Потому что вы добавляете новый экземпляр того же класса в коллекцию, которая уже содержит этот экземпляр.

+0

Я получаю такую ​​же ошибку даже после использования. –

+0

Да, это из-за «нового HexaObserver», о котором я упоминаю в своем ответе. Вы повторяете и изменяете одну и ту же коллекцию из одного потока. – Dariusz

0

ConcurrentModificationException, как правило, признак того, что вы использовали Iterator на Collection и что в то время как итерация, вы также модифицировали, лежащий в основе Collection (помните, что Еогеасп-выражение фактически ярлык для использования 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); 
    } 
} 
Смежные вопросы