Java - SwingWorker - 如何在EDT中使用发布中的arraylist

时间:2017-08-01 13:43:04

标签: java user-interface arraylist swingworker

在访问另一个线程(SwingWorker)中正在处理的列表时,如何避免java.util.ConcurrentModificationException

我正在尝试使用的详细信息:

  • 包含这种“主要”方法的GUI类,我认为应该在EDT上运行。

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new myGUIwithButton();
            }
        });
    }
    
  • 此GUI有一个paint方法,它采用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
                }
    
  • 在GUI中按下按钮时,将执行扩展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);
    }
    

有什么问题?

当工作线程“快速”(暂停小于50ms)时,我经常会遇到paint方法中的第170行的异常(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)

在EDT正在处理时,似乎正在修改words方法中的paint列表。我不是在那里修改它,所以它必须来自另一个线程?
我认为该列表只是另一个线程中words列表的“快照”,因为它是使用publish方法发送的。显然它不是。 那么我应该使用SwingWorker发布的列表的“快照”来改变EDT方法的工作原理?

感谢您提供任何建议。

备注

  • 异常后,程序运行正常。我只想让它变得更干净。
  • 我试着查看Collections.synchronizedList()甚至是synchronized (words){...},但我的所有尝试都失败了,很可能是因为我不知道“同步”在这种情况下意味着什么,以及如何使用它。
  • 请注意,paint方法中的invokeLater一开始似乎没用,因为我总是从EDT调用它,但是如果我不使用它,paint的第一次调用就不起作用了创建GUI(是因为它在GUI之前执行,尽管被调用之后?
  • 我显然缺乏很多想法,所以细节会非常感激。

1 个答案:

答案 0 :(得分:2)

通过发布在SwingWorker中创建的words列表并继续处理同一实例,您可以使此实例同步。您应该更改doInBackground方法,为发布创建新的List,如下所示:

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;
}

通过此更改,后台作业和绘制方法正在处理不同的对象,您的问题应该得到解决。