我遇到一个问题,我有一个JTable和一个自定义模型,在渲染阶段修改模型时会出现并发访问问题。我收到如下的异常,因为我假设它获取表的长度,模型更新,然后它访问不存在的模型元素。 AbstractTableModel需要在渲染期间使用行/列索引重新访问模型以获取所需信息,并且似乎没有任何锁定,这意味着数据可以自由更改。
Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
at java.util.LinkedList.checkElementIndex(LinkedList.java:553)
at java.util.LinkedList.get(LinkedList.java:474)
at koku.ui.PlayerList$PlayerInfoTblModel.getValueAt(PlayerList.java:250)
at javax.swing.JTable.getValueAt(JTable.java:2720)
at javax.swing.JTable.prepareRenderer(JTable.java:5718)
at javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2117)
at javax.swing.plaf.basic.BasicTableUI.paintCells(BasicTableUI.java:2019)
at javax.swing.plaf.basic.BasicTableUI.paint(BasicTableUI.java:1815)
at javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
at javax.swing.JComponent.paintComponent(JComponent.java:778)
at javax.swing.JComponent.paint(JComponent.java:1054)
at javax.swing.JComponent.paintChildren(JComponent.java:887)
at javax.swing.JComponent.paint(JComponent.java:1063)
at javax.swing.JViewport.paint(JViewport.java:725)
at javax.swing.JComponent.paintChildren(JComponent.java:887)
at javax.swing.JComponent.paint(JComponent.java:1063)
at javax.swing.JComponent.paintChildren(JComponent.java:887)
at javax.swing.JComponent.paint(JComponent.java:1063)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5206)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:295)
at javax.swing.RepaintManager.paint(RepaintManager.java:1217)
at javax.swing.JComponent._paintImmediately(JComponent.java:5154)
at javax.swing.JComponent.paintImmediately(JComponent.java:4964)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:781)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:739)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:688)
at javax.swing.RepaintManager.access$700(RepaintManager.java:59)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1632)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:660)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
想知道解决这个问题的最佳方法是什么。
干杯,
克里斯
答案 0 :(得分:4)
应始终从AWT线程更新Swing组件/模型,而不是从其他线程更新。
有关长时间运行的任务,请参阅SwingUtilities.invokeLater和SwingWorker
答案 1 :(得分:2)
这是我的方法:
总之,更新表模型和刷新JTable(以及其他视图,如果有的话)的过程是一个原子操作。为实现这一目标,我们有一个单独的缓存模型支持我们的表格,该表格仅在EDT上更新。与Swing相关的所有内容都变为单线程,并且通过使用invokeLater(),我们确保仅在完全处理当前事件后才处理下一个事件。
如何进一步改善这一点:
fireTableXxxChanged()
来通知正在侦听的JTable,从而对此类通知作出反应。最后你会有这个链:
tableModel.fireTableXxxChanged()
)来回应EDT模型事件这种方法允许您将GUI与业务逻辑完全分离(在此处识别3层系统:GUI,业务逻辑和持久性),这非常强大。在设计良好的系统中,所有命令都在第二层执行,您可以非常轻松地创建多个控制器。例如,在GUI中,可以使用Swing控件来操作应用程序状态,还可以创建一个命令行,您只需键入命令即可。这对于业务逻辑的脚本/自动化测试以及GUI如何对业务逻辑的变化做出反应非常方便。
最后,它会得到回报,但它肯定需要大量的额外工作和艰苦的思考来做正确的事。
答案 2 :(得分:1)
我建议使用Glazed Lists进行所有TableModel访问:http://www.glazedlists.com/
我已经在一些项目上使用它们来完成一些相当繁重的数据,并且它运行得很完美。它将TableModels抽象为一个ArrayList,您可以将其包装在SynchronizedTableLists和FilteredLists中,这样可以非常轻松,安全地执行各种非常复杂的操作。
您还可以添加监听器并收到有关对TableModel的修改的通知
答案 3 :(得分:0)
如果要进行并发访问,则需要同步模型。 尝试阅读教程 http://download.oracle.com/javase/tutorial/essential/concurrency/ 祝你好运
PS:有时您可以为您的软件考虑另一种解决方案而不是并发访问。另外,为了获得更好的答案,您可以发布一些应用程序代码。