Java: is there a SwingUtilities.invokeNowOrLaterIfEDT(...) or similar?
(be sure to read the edit below, this question is obviously confusing I'm sorry about that)
Here's a typical SwingUtilities.invokeLater
call:
SwingUtilities.invokeLater( new Runnable() {
public void run() {
...
}
} );
Now I'd like to have what would be a SwingUtilities.invokeNowOrLaterIfEDT
.
Of course I could use my own utility class, like this:
public static void invokeNowOrLaterIfEDT( @NotNull R开发者_如何学运维unnable r ) {
if ( SwingUtilities.isEventDispatchThread() ) {
Thread t = new Thread( r );
t.start();
} else {
r.run();
}
}
but this has the drawback of creating a new thread everytime this is called from the EDT (and having to wait for that new thread to be scheduled).
Another way to do it would be to use one additional thread running during the lifecycle of the app (which could be lazily instantiated) using some blocking queue to enqueue the Runnables
. The benefit would be that there wouldn't constantly be threads created everytime I'd call such a method from the EDT.
Does something like this exist in the default Java APIs?
What would be better: simply creating a new thread each time this is called from the EDT like in the example above or have one additional thread in which Runnables
to be executed outside the EDT would be enqueued or something else?
EDIT: Just like SwingUtilities.invokeLater
may or may not be called from the EDT, the method I had in mind may or may not be called from the EDT. It is to execute something completely asynchronous that may or may not be triggered from a user action on the GUI / EDT. I think SwingUtilities.invokeLater
is very convenient to be able to run something in the EDT without having to care when you're invoking it if you're on the EDT or not. And I think that the invokeNowOrLaterIfEDT(...)
that I showed above is very convenient. But I may be mistaken on that. Is the need for such a method crazy? Now I'm starting to doubt!?
EDIT TWO: I haven't been clear at all and I realize it, sorry about that. The things I want to run asynchronously not from the EDT are not super long process and there's no need to have any update like progress bar or anything showing what's going on. It's also not a problem if it's single-threaded/queued (obviously, I stated it in the question). I'm not at all after a thread pool to efficiently spread the load on various cores etc. It's just that these are small computation that do not need to be run on the EDT and that may be called from the EDT or not. It's really just tiny asynchronous stuff I want to execute outside the EDT. It's not an issue if they're all queued on the same thread etc.
But now, thanks to your answers, I also realize that if such a thing was implemented using a single thread and a Runnable queue, then it would be very easy to shot oneself in the foot by calling such a utility method for long computation that would then all be queued instead of correctly multi-threaded.
- Are you sure this is what you really want to do, because in any case in your method a runnable will never be run on EDT.
- Now for your anwser, depening on how often and from how many threads this method could be called, I think you should use a ThreadPool. I would suggest using ThreadPoolExecutor and other APIs in the concurrent package.
One advantage of using the java.util.concurrent API like ThreadPoolExecutor and Executors is that you can fine tune a lot of parameters and the entire pooling system by simply calling direct API's and no extra effort.
Another option (in addition to Suraj's) is to use a SwingWorker
.
This class is specifically designed for what you want to do: take a (possibly long running) process from the EDT and execute it in a separate thread. The SwingWorker
has the advantage of handling all the thread context switches, and also allows you to provide updates back to the EDT (for example, to display a progress bar), without having to worry about the threading yourself.
It seems like your question boils down to "is it better to create a new thread for every asynchronous call, or use a dedicated thread/thread pool"?
Unfortunately, I think the answer is "it depends". If you know these tasks are short and rare, a new thread is probably fine. If you're worried about long computations blocking each other, you probably want a thread pool.
static ExecutorService ex = Executors.newCachedThreadPool();
public static void invokeOutsideEDT(Runnable r) {
if (SwingUtilities.isEventDispatchThread()) {
ex.submit(r);
} else {
r.run();
}
}
As Suraj noted, the concurrent
package gives you a lot of off-the-shelf choices here. Executors.newCachedThreadPool()
I've used above will add threads as needed, reusing old threads; but you could try Executors.newSingleThreadExecutor()
instead and see if the long-running tasks are really a problem, or use Executors.newFixedThreadPool()
with a set number of threads to make sure you can always handle n
concurrent tasks even if some of them are long-running.
精彩评论