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 method doInBackground

    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 call paint

    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.
+3


source to share


1 answer


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.

+2


source







All Articles