在访问另一个线程(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方法的工作原理?
感谢您提供任何建议。
synchronized (words){...}
,但我的所有尝试都失败了,很可能是因为我不知道“同步”在这种情况下意味着什么,以及如何使用它。invokeLater
一开始似乎没用,因为我总是从EDT调用它,但是如果我不使用它,paint
的第一次调用就不起作用了创建GUI(是因为它在GUI之前执行,尽管被调用之后?答案 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;
}
通过此更改,后台作业和绘制方法正在处理不同的对象,您的问题应该得到解决。