使GUI更具响应性

时间:2010-02-18 07:02:28

标签: java user-interface swingworker

我已经可视化物理模拟的程序(基本上)。现在,它可以工作,但可以变得非常反应,我想我知道为什么 - 太多(读取:全部)计算是在事件线程上完成的。

当按下“播放”按钮时,会创建一个Swing Timer,它会定期唤醒并调用updateTime() - 到目前为止一直很好。问题是updateTime()遍历每个依赖于时间的对象,并告诉它以适当的量(实时过去或每个滴答的任意时间单位)及时向前传播。这些计算以及随后的GUI更新都在事件派发线程上。

所以,我想尽可能多地卸载这个计算,我认为SwingWorker是可行的方法,但我不确定如何将它们用于现有代码。我不能让现有的类扩展SwingWorker,因为其中许多类已经扩展了其他类。

到目前为止,我最好的想法是为每个与时间相关的对象创建一个接口来实现。界面将指定2个方法calcUpdateTime()drawUpdateTime()。我会将每个当前的updateTime()方法拆分为物理计算(转换为calc_)和GUI更新(转换为draw_)。然后我只创建一个SwingWorker类,它在构造函数中使用TimeDependant对象,其doInBackground将调用calcUpdateTimedone将调用drawUpdateTime。那么我可以替换所有出现的

myObj.updateTime(currentTime);

new MySwingWorker(myObj, currentTime).execute();

我想通过SO来运行这个想法,因为它感觉不太正确,并且它希望避免重构整个项目,只是为了找出我从一个坏主意开始。另外,每次打勾可能会创建几十个MySwingWorker,这不是一个坏主意吗?

感谢您阅读此内容。

2 个答案:

答案 0 :(得分:2)

你是对的,没有必要为每个工作人员调用SwingWorker.execute(),因为你将创建并销毁许多你真正不需要的线程。

然而,使用SwingWorker仍然是一个好主意,只是因为它为您提供了一种简单的方法来将需要在后台运行的代码(您的SwingWorker.doInBackground()的实现)与之后需要在Swing中运行以更新GUI的代码(SwingWorker.done()的实现)。

我建议使用javax.swing.Timer,而不是使用java.util.Timerjava.util.concurrent.ScheduledThreadPoolExecutor。基本上,它可以执行java.util.Timer可以执行的所有操作,除了它还使您有机会控制在后台运行的线程数,如何处理在后台线程中引发的异常,等等。

答案 1 :(得分:0)

我使用Swing计时器的体验很差(性能方面)。由于Swing中的所有事件都使用相同的线程,因此似乎有很多意外的延迟。

我还建议相信你对每个嘀嗒声的多个摇摆工作者实例的直觉:这对我来说也不合适。 (根据SwingWorker文档,它只能运行一次,因此您无法安全地重复使用它。

可能会执行您需要的计时器是java.util.Timer。这有很多用于指定超时的选项,尽管取决于时序仿真的保真度,即使这可能也不合适。 (例如:如果您希望它实时运行,但您的计算实际上需要比实时更长的时间,它应该做什么?慢慢运行,或者开始跳过时间步骤?)

因此,我不知道您的计算/绘图程序究竟涉及什么,我暂时建议尝试java.util.Timer。当它到期时(根据您的“实时乘数”,在您认为合适的任何超时期限之后),运行所有计算,然后将结果返回到EDT线程以进行绘制(例如,将它们包装在一个SwingUtilities.invokeLater())。

当然,如果EDT想要引用与计算线程相同的对象,这可能会引入锁定问题。理想情况下,如果calc线程可以将不可移动的结果传递给EDT,则可以节省必须引入锁。

(免责声明:以上都没有真正考虑多个核心/处理器,除了一个用于GUI,一个用于计算。如果你想并行化应用程序,它可能不是一个合适的解决方案。)