JTable中的并发问题
我有一个问题,我有一个 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)
想知道解决这个问题的最佳方法是什么。
干杯,
克里斯
I have a problem where I have a JTable and a custom model, with concurrent access problems when the model is modified during the rendering phase. I receive an exception like the following, because I assume that it gets the length of the table, the model is updated, and then it accesses a model element that doesn't exist. The AbstractTableModel needs to reaccess the model using a row / column index during rendering to get the required information, and there doesn't seem to be any locking around this, meaning the data can change freely.
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)
Wondering what the best way to solve this problem is.
Cheers,
Chris
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是我的方法:
总之,更新表模型和刷新 JTable(以及其他视图,如果有)的过程是一个原子操作。为了实现这一点,我们有一个单独的缓存模型来支持我们的表,该模型仅在 EDT 上更新。与 Swing 相关的所有内容都变成单线程,并且通过使用 invokeLater(),我们可以确保仅在当前事件完全处理后才处理下一个事件。
如何进一步改进:
fireTableXxxChanged()
来通知正在侦听的 JTable,从而对此类通知作出反应。最后,您将拥有以下链:
tableModel.fireTableXxxChanged()
),这种方法允许您将 GUI 与业务逻辑完全分离(这里认识一下 3 层系统:GUI、业务逻辑和持久性),这是非常强大的。在一个设计良好的系统中,所有命令都在第二层执行,您可以非常轻松地创建多个控制器。例如,在 GUI 中,可以使用 Swing 控件来操纵应用程序状态,还可以创建一个命令行,您只需在其中键入命令。这对于业务逻辑的脚本化/自动化测试以及 GUI 如何对业务逻辑的变化做出反应非常方便。
最终,它得到了回报,但它肯定需要大量额外的工作和认真的思考才能做好。
This is my approach:
In summary, the process of updating the Table Model and refreshing the JTable (and other views, if any) is made an atomic operation. To achieve this, we have a separate cache model backing our tabel, which is updated only on the EDT. Everything Swing-related becomes single threaded, and by using invokeLater() we are sure that the next event is processed only after the current event has been fully processed.
How to improve this even further:
fireTableXxxChanged()
.In the end you would have this chain:
tableModel.fireTableXxxChanged()
)This approach allows you to completely separate GUI from business logic (recognize the 3 layer system here: GUI, Business Logic and Persistence), which is very powerful. In a well-designed system where all commands are executed in the 2nd layer, you can create multiple controllers very easily. For example, in a GUI, it becomes possible to manipulate the application state using Swing controls, but also to create a command line where you can just type the commands. This would come in very handy for scripted/automated testing of the business logic, and how the GUI reacts on changes in the business logic.
In the end, it pays off, but it definately requires a lot of extra work and hard thinking to do it right.
Swing 组件/模型应始终从 AWT 线程更新,而不是从另一个线程更新。
请参阅 SwingUtilities。 invokeLater 和 SwingWorker 用于长时间运行的任务
Swing component/models should always be updated from the AWT thread, and never from another thread.
See SwingUtilities.invokeLater, and SwingWorker for long running tasks
我建议对所有 TableModel 访问使用 Glazed Lists: http://www.glazedlists.com/
我'我们已经在许多项目中使用它们来处理一些相当繁重的数据,并且它运行得非常完美。它将 TableModel 抽象为 ArrayList,您可以将其包装在 SynchronizedTableLists 和 FilteredLists 中,让您可以非常轻松且安全地完成各种真正复杂的事情。
您还可以添加侦听器并获取对 TableModel 的修改的通知
I'd suggested using Glazed Lists for all TableModel access: http://www.glazedlists.com/
I've used them on a number of projects for some pretty heavy lifting of data and it has worked flawlessly. It abstracts TableModels to an ArrayList which you can wrap in SynchronizedTableLists and FilteredLists that let you do all sorts of really complicated things very easily and safely.
You can also add Listeners and get notified of modifications to the TableModel
如果您要进行并发访问,您需要同步您的模型。
尝试阅读教程
http://download.oracle.com/javase/tutorial/essential/concurrency/
祝你好运
PS:有时您可以为您的软件想到另一种解决方案,而不是并发访问。另外,为了获得更好的答案,您可以发布一些应用程序的代码。
if you are going to do concurrent access you need to synchronize your model.
try reading the tutorial
http://download.oracle.com/javase/tutorial/essential/concurrency/
good luck
PS: Sometimes you can think of another solution for your software than going for concurrent access. Also, to get better answers you could post some code of your application.