我已经可视化物理模拟的程序(基本上)。现在,它可以工作,但可以变得非常反应,我想我知道为什么 - 太多(读取:全部)计算是在事件线程上完成的。
当按下“播放”按钮时,会创建一个Swing Timer
,它会定期唤醒并调用updateTime()
- 到目前为止一直很好。问题是updateTime()
遍历每个依赖于时间的对象,并告诉它以适当的量(实时过去或每个滴答的任意时间单位)及时向前传播。这些计算以及随后的GUI更新都在事件派发线程上。
所以,我想尽可能多地卸载这个计算,我认为SwingWorker
是可行的方法,但我不确定如何将它们用于现有代码。我不能让现有的类扩展SwingWorker
,因为其中许多类已经扩展了其他类。
到目前为止,我最好的想法是为每个与时间相关的对象创建一个接口来实现。界面将指定2个方法calcUpdateTime()
和drawUpdateTime()
。我会将每个当前的updateTime()
方法拆分为物理计算(转换为calc_
)和GUI更新(转换为draw_
)。然后我只创建一个SwingWorker类,它在构造函数中使用TimeDependant
对象,其doInBackground
将调用calcUpdateTime
而done
将调用drawUpdateTime
。那么我可以替换所有出现的
myObj.updateTime(currentTime);
与
new MySwingWorker(myObj, currentTime).execute();
我想通过SO来运行这个想法,因为它感觉不太正确,并且它希望避免重构整个项目,只是为了找出我从一个坏主意开始。另外,每次打勾可能会创建几十个MySwingWorker
,这不是一个坏主意吗?
感谢您阅读此内容。
答案 0 :(得分:2)
你是对的,没有必要为每个工作人员调用SwingWorker.execute()
,因为你将创建并销毁许多你真正不需要的线程。
然而,使用SwingWorker
仍然是一个好主意,只是因为它为您提供了一种简单的方法来将需要在后台运行的代码(您的SwingWorker.doInBackground()
的实现)与之后需要在Swing中运行以更新GUI的代码(SwingWorker.done()
的实现)。
我建议使用javax.swing.Timer
,而不是使用java.util.Timer
或java.util.concurrent.ScheduledThreadPoolExecutor
。基本上,它可以执行java.util.Timer
可以执行的所有操作,除了它还使您有机会控制在后台运行的线程数,如何处理在后台线程中引发的异常,等等。
答案 1 :(得分:0)
我使用Swing计时器的体验很差(性能方面)。由于Swing中的所有事件都使用相同的线程,因此似乎有很多意外的延迟。
我还建议相信你对每个嘀嗒声的多个摇摆工作者实例的直觉:这对我来说也不合适。 (根据SwingWorker
文档,它只能运行一次,因此您无法安全地重复使用它。
可能会执行您需要的计时器是java.util.Timer
。这有很多用于指定超时的选项,尽管取决于时序仿真的保真度,即使这可能也不合适。 (例如:如果您希望它实时运行,但您的计算实际上需要比实时更长的时间,它应该做什么?慢慢运行,或者开始跳过时间步骤?)
因此,我不知道您的计算/绘图程序究竟涉及什么,我暂时建议尝试java.util.Timer
。当它到期时(根据您的“实时乘数”,在您认为合适的任何超时期限之后),运行所有计算,然后将结果返回到EDT线程以进行绘制(例如,将它们包装在一个SwingUtilities.invokeLater()
)。
当然,如果EDT想要引用与计算线程相同的对象,这可能会引入锁定问题。理想情况下,如果calc线程可以将不可移动的结果传递给EDT,则可以节省必须引入锁。
(免责声明:以上都没有真正考虑多个核心/处理器,除了一个用于GUI,一个用于计算。如果你想并行化应用程序,它可能不是一个合适的解决方案。)