Swing StyleGroupSet并发修改异常问题及解决方法咨询
解决Swing集成GraphStream时的ConcurrentModificationException异常
问题根因
你的代码存在跨线程并发修改Graph集合的问题:
Simulator类的start方法里,while循环运行在main线程中,每次调用updateFrame直接修改Graph(添加/删除边)。- 你使用的
Viewer.ThreadingModel.GRAPH_IN_GUI_THREAD模型,会让GraphStream在AWT事件调度线程(EDT)中执行渲染逻辑,遍历Graph的元素集合。 - 当main线程修改集合的同时,EDT线程正在遍历它,直接触发了
ConcurrentModificationException。
解决方案
所有对Graph的修改操作必须放到**AWT事件调度线程(EDT)**中执行,同时遵循Swing单线程模型规范,推荐两种实现方式:
方式1:用Swing Timer替代while循环
Swing Timer的任务会自动在EDT中执行,完美匹配Swing和GraphStream的线程要求:
修改Simulator类的start方法:
public void start() { SwingUtilities.invokeLater(() -> { graphFrame = new GraphFrame(); graphFrame.setVisible(true); graphFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); }); // 用Swing Timer替代while循环,每隔1秒执行一次更新 Timer timer = new Timer(1000, e -> { if (graphFrame != null) { graphFrame.updateFrame(); } }); timer.start(); }
同时修正GraphFrame的updateFrame方法(修复原删除逻辑的索引越界问题):
public void updateFrame(){ SwingUtilities.invokeLater(() -> { addRandomEdge(); // 先获取所有边的列表,再随机删除其中一条 if (!graph.getEdgeSet().isEmpty()) { List<Edge> edges = new ArrayList<>(graph.getEdgeSet()); graph.removeEdge(edges.get(rand.nextInt(edges.size()))); } pipeIn.pump(); }); }
方式2:在修改Graph时强制切换到EDT
如果必须保留while循环,需要把所有Graph修改操作通过SwingUtilities.invokeLater包装:
修改Simulator的while循环部分:
while(true){ try { Thread.sleep(1000L); } catch (InterruptedException e) { throw new RuntimeException(e); } // 把更新操作放到EDT执行 SwingUtilities.invokeLater(() -> graphFrame.updateFrame()); }
同步修正GraphFrame的updateFrame中的删除逻辑:
public void updateFrame(){ addRandomEdge(); if (!graph.getEdgeSet().isEmpty()) { List<Edge> edges = new ArrayList<>(graph.getEdgeSet()); graph.removeEdge(edges.get(rand.nextInt(edges.size()))); } pipeIn.pump(); }
额外注意点
- 原代码中
graph.removeEdge(rand.nextInt(graph.getNodeSet().size()))存在逻辑错误:removeEdge需要传入边的ID或Edge对象,你传入的是节点数量范围内的随机数,大概率找不到对应边,甚至可能抛出异常,必须修正为基于边集合的随机删除逻辑。 - GraphStream的
GRAPH_IN_GUI_THREAD模型要求Graph的所有修改都在EDT中进行,这是避免并发问题的核心前提。
内容的提问来源于stack exchange,提问作者Kfir Ettinger
相关产品推荐
相关产品推荐

