Java - SwingWorker - How to use arraylist from EDT post
How can I avoid java.util.ConcurrentModificationException
when accessing a list that is being processed in another thread (SwingWorker)?
Details on what I am trying to use:
-
the GUI class that contains this "main" method, and I think it should work on EDT.
public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new myGUIwithButton(); } }); }
-
This GUI has a draw method that takes a list
Words
, a class containing strings and coordinates, and displays them:public void paint(final List<Word> words){ SwingUtilities.invokeLater(new Runnable() { public void run() { // prepare GUI 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 }
-
When the button is clicked in the GUI, an instance of the class extending is executed
SwingWorker<List<Word>,List<Word>>
. As I understand it, this creates a worker thread. The overridden methoddoInBackground
creates a Word list and publishes it regularly: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; }
-
The published words are then automatically sent to the overridden method
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); }
What's the problem?
When the worker thread is "fast" (less than 50ms pause), I often get an exception for line 170, which is in the method paint
(the GUI file is called 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)
It looks like the list Words
in the method is paint
changing while EDT is working on it. I am not changing it there, so it must be from a different thread?
I thought that this list was just a "snapshot" of a list Words
in another thread, since it was posted using the method publish
. Apparently not.
So what should I change to work in the EDT method with such a "snapshot" of the published list from SwingWorker
?
Thanks for any advice you could provide.
Notes
- After the exception, the program continues to run normally. I just wish I could make it cleaner.
- I've tried looking at Collections.synchronizedList () or even
synchronized (words){...}
, but all my attempts have failed, most likely because I have no idea what "synchronicity" means in this case and how to use it. - Note that the method
invokeLater
in the draw method at first seemed useless because I always call it from the EDT, however if I don't use it, the first callpaint
does not work when the GUI is created (this is because it is executed in front of the GUI, even though what is it called after? - I am clearly missing a lot of concepts, so the details will really be appreciated.
source to share
words
When publishing a list created in SwingWorker and continuing to work on the same instance, you must synchronize that instance. You must change the method doInBackground
to create a new one List
for posting like this:
public List<Word> doInBackground() {
List<Word> words = new ArrayList<Word>();
while (!isCancelled()) {
// do some work, define aNewWord
words.add(aNewWord);
publish(new ArrayList<>(words)); // don't publish words directly but create new list
Thread.pause(someTime);
}
return words;
}
With this change, background work and your drawing method are working on separate objects and your problem should be resolved.
source to share