The program I have visualizes a physics simulation (basically). Right now, it works, but can get very unresponsive, and I think I know why - too much (read:all) computation is done on the event thread.
When the 'play' button is pressed, a Swing Timer
is created that wakes up periodically and calls updateTime()
- so far so good. The problem is that updateTime()
iterates through every time-dependent object and tells it to propagate itself forwards in time by the appropriate amount (either the real time elapsed, or an arbitrary time unit each tick). These calculations, and the subsequent GUI updates, are all on the event dispatch thread.
So, I'd like to offload as much of this computation as possible, and I think SwingWorker
s are the way to go, but I'm not sure how to work them into the existing code. I can't have the existing classes extend SwingWorker
since many of them already extend other classes.
My best idea so far is to create an interface for each time-dependent object to implement. The interface would specify 2 methods, calcUpdateTime()
and drawUpdateTime()
. I would split up each of their current updateTime()
methods into the physics calculations (into calc_
) and GUI updates (into draw_
). Then I would create only one class of SwingWorker that takes a TimeDependant
object in the constructor, and its doInBackground
would call calcUpdateTime
and done
would call drawUpdateTime
. So then I can replace all occurrences of
myObj.updateTime(currentTime);
with
new MySwingWorker(myObj, currentTime).execute();
I wanted to r开发者_如何学编程un this idea by SO because it doesn't feel quite right, and it would like to avoid refactoring the entire project just to find out I started out with a bad idea. Also, isn't it probably a bad idea to create potentially dozens of MySwingWorker
s each tick?
Thanks for reading this far.
You're right, it's not necessary to call SwingWorker.execute()
for every worker for every tick, because you'll be creating and destroying a lot of threads that you really don't need.
However, using a SwingWorker
is still a good idea, just because it gives you an easy way to separate the code that needs to be run in the background (your implementation of SwingWorker.doInBackground()
) from the code that needs to be run in Swing to update the GUI afterwards (your implementation of SwingWorker.done()
).
Rather than using javax.swing.Timer
or java.util.Timer
, I would recommend using java.util.concurrent.ScheduledThreadPoolExecutor
. Basically, it can do everything that a java.util.Timer
can do, except that it also gives you the opportunity to control how many threads are working in the background, how to handle exceptions that are thrown in background threads, and so forth.
I've had poor experiences (performance-wise) using the Swing timer. Since all events across Swing use the same thread, it seems to have unexpected delays quite a lot.
I'd also suggest to trust your instincts about multiple swing worker instances every tick: it doesn't sound right to me either. (And according to the SwingWorker
docs, it's designed to only be run once, so you can't necessarily reuse it safely).
A timer that might do what you need is the java.util.Timer
. This has quite a lot of options for specifying timeouts, although depending on the fidelity of your timing simulation even this may not be appropriate. (For example: if you want it to run in real time, but your calculations actually take longer than real time, what should it do? Run slowly, or start skipping time steps?)
So, not knowing exactly what your calc/draw routines involve, I'd tentatively suggest trying a java.util.Timer
. When it expires (after whatever timeout period you deem appropriate based on your "realtime multiplier"), run through all the calcs, and then throw the results back to the EDT thread to do the drawing (for example, by wrapping them up in a SwingUtilities.invokeLater()
).
Of course, this maybe introduce locking issues if the EDT wants to references the same objects as the calculation thread. Ideally if the calc thread can pass immuatable results to the EDT, it would save having to introduce locks.
(A disclaimer: none of the above really takes into account multiple cores/processors either, other than one for GUI, one for calculations. If you want to parallelise the application, it's probably not an appropriate solution).
精彩评论