Как избежать java.util.ConcurrentModificationException при доступе к списку, который обрабатывается в другом потоке (SwingWorker)?
Подробности о том, что я пытаюсь использовать:
класс GUI, который содержит этот «основной» метод, и я думаю, что он должен работать в EDT.
public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new myGUIwithButton(); } }); }Этот графический интерфейс имеет метод рисования, который берет список
Words, класса, содержащего строки и координаты, и показывает их:public void paint(final List<Word> words){ SwingUtilities.invokeLater(new Runnable() { public void run() { // prepare GUI's background, then : for(Word word : words){ // <-- this is line 170 in the error shown below // show each word in the GUI // note that I'm not modifying the words list here }При нажатии кнопки в графическом интерфейсе выполняется экземпляр класса, расширяющего
SwingWorker<List<Word>,List<Word>>. Это создает рабочий поток, насколько я понимаю. Переопределенный методdoInBackgroundсоздает список Word, а затем регулярно его публикует:public List<Word> doInBackground() { List<Word> words = new ArrayList<Word>(); while (!isCancelled()) { // do some work, define aNewWord words.add( aNewWord ); publish(words); Thread.pause(someTime); } return words; }Затем опубликованные слова автоматически отправляются в переопределенный метод
process:protected void process(List< List<Word> > wordss) { // Executed on EDT ! <3 // I'm taking only the first list that was published to avoid trouble List<Word> words = wordss.get(0); myGUI.paint(words); }
В чем проблема?
Когда рабочий поток идет «быстро» (пауза менее 50 мс), я часто получаю исключение для строки 170, которая находится в методе paint (файл GUI называется MotsFleches.java):
Exception in thread "AWT-EventQueue-1" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at MotsFleches$2.run(MotsFleches.java:170)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at org.GNOME.Accessibility.AtkWrapper$5.dispatchEvent(AtkWrapper.java:700)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Похоже, что список words в методе paint модифицируется, пока над ним работает EDT. Я не изменяю его там, поэтому он должен быть из другого потока?
Я думал, что список был просто "моментальным снимком" списка words в другом потоке, так как он был отправлен методом publish. Видимо это не так. Так что же мне изменить, чтобы работать в методе EDT с таким "снимком" опубликованного списка из SwingWorker?
Спасибо за любой совет, который вы могли бы дать.
Примечания
- После исключения программа продолжает работать нормально. Я только хотел бы сделать его чище.
- Я попытался просмотреть Collections.synchronizedList() или даже
synchronized (words){...}, но все мои попытки не увенчались успехом, скорее всего потому, что я понятия не имею, что в данном случае означает "синхронизм" и как его использовать. - Обратите внимание, что
invokeLaterв методе рисования поначалу казался бесполезным, потому что я всегда вызываю его из EDT, однако, если я его не использую, первый вызовpaintне работает при создании графического интерфейса (это потому, что он выполняется до графический интерфейс, несмотря на то, что он вызывается после ? - Мне явно не хватает многих понятий, поэтому подробности будут действительно оценены.
doInBackgroundи передайте егоpublish. Посмотрите, поможет ли это. - person user1803551   schedule 01.08.2017