我的应用程序中有很多JTree,它们都使用同一个自定义TreeModel类的不同实例(具有不同的选项)。我正在尝试添加一些线程来加快速度,因为getChildren()成员需要一段时间才能运行,所以我在TreeModel中添加了一个SwingWorker子类,并在getChildren()中创建了该worker的一个实例并执行它,然后返回它的get()结果。
我不断得到ConcurrentModificationExceptions,我知道我应该同步一些东西,但不确定是什么。有没有人对如何让我的TreeModel安全响应有什么建议?
谢谢!约书亚
发布于 2009-05-11 00:43:44
首先,我想知道您为什么要尝试使用线程来提高速度。是CPU绑定的操作,还是网络/磁盘绑定的操作?
您的描述看起来像是您的getChildren在等待操作完成后才返回。这可能对多核硬件上的并行操作很有用,但不是通用模型。通常我们(和SwingWorker所做的)是在另一个线程上运行代码块,而事件分派线程可以自由运行,直到它接收到一个事件来处理来自后台线程的数据。
SwingWorker没有说明它抛出了ConcurrentModificationException anywhere。堆栈跟踪可能会有所帮助。
我通常建议避免使用SwingWorker。它很适合创建一个小的演示操作,但却强加了一个很差的设计。
线程是困难的,特别是现在多线程硬件实际上有更多的bug。随意使用synchronized并不会让问题消失。一种相对简单的通用方法是避免共享可变对象(或至少更改共享对象)。将EDT中未使用的一组参数传递到后台线程。同样,将未被修改的参数传递回EDT。
发布于 2009-05-11 01:47:55
当你在两个线程上访问相同的列表时,就会发生ConcurrentModificationExceptions。您的TreeModel可能使用ArrayLists、Vectors、Hashtables或类似的东西来存储节点。
我的猜测是发生了以下两件事之一:
1)您的TreeModel在每次呈现时都会不断地被EDT线程上的JTree查询。这是不可避免的,也是Swing的工作原理。如果您正在访问树模型或位于不同线程上的底层列表,则偶尔会在呈现和列表抛出异常的同时执行此操作。
2)您有两个swing工作线程在运行,每个都在各自的线程上。它们同时更新/查询列表。和上面的问题一样。
我同意汤姆的观点,使用SwingWorker使你的TreeModel“异步”是一个非常棘手的问题,应该避免。但是,为了回答您的问题,我将查看以下代码。请注意,对树的所有查询和更新操作都是在"finished“方法中进行的,该方法总是在EDT上运行。我的猜测是您在construct()方法中执行get/set。
public TreeNode[] getChildren(TreeNode parent){
// Do all normal work you would do
...
// figure out if this parents children have not been fetched yet
boolean needToFetch = ....
if(needToFetch){
worker = new SwingWorker() {
public Object construct() {
// go fetch your children from whatever database, server, file...
// fetchNewChildren must NOT access the TreeModel or any of the underlying
// lists, because you will get a modification exception. This is what
// Tom meant by "Pass a set of arguments into the background thread
// that are not being used in the EDT"
return fetchNewChildNodes(parent);
}
public void finished() {
List<TreeNode> newNodes = (List<TreeNode>)get();
// insert the newly fetched nodes into the parent
// if done correclty, this will fire TreeModelChanged events
// so the tree should re-render, repaint, etc...
parent.addChildren(newNodes);
}
};
worker.start();
} 发布于 2009-05-11 02:10:16
也许可以尝试将数据存储在CopyOnWriteArrayList中的TreeModel中
https://stackoverflow.com/questions/846317
复制相似问题