Java - SwingWorker - problem
I am developing a Java Desktop Application. This application performs the same task public class MyTask implements Callable<MyObject> {
on multiple threads at the same time.
Now when the user clicks the start button, I have created SwingWorker myWorker
and executed it.
Now this one myWorker
creates multiple instances MyTask
and sends them to ExecutorService
.
Each instance MyTask
has a loop and generates an intermediate result on each iteration. Now I want to collect these intermediate results from each instance MyTask
as soon as they are generated. Then, after collecting these intermediate results from each instance MyTask
, I want to post it through SwingWorker.publish(MyObject)
so the progress is shown on the EDT.
Q1. How to implement this? Should I MyTask
subclass SwingWorker
instead Callable
to get intermediate results, because I think it Callable
only returns the final result.
Q2. If the answer is Q1. yes then can you give me a small example to show how I can get these intermediate results and populate them and then post them from the main one SwingWorker
?
Q3. If I cannot use SwingWorker
in this situation, then how to implement it?
a source to share
Have a look at ExecutorCompletionService <T> . It is an Executor who supplies a method take
to get the result of any completed task.
Update:
The extension SwingWorker
won't do what you want as it is specifically designed to offload EDT work to a background thread. You cannot use it to offload work from a background thread to other background threads. The calls SwingWorker.publish
result in the equivalent of a SwingUtilities.invokeLater
. The mechanism I am aware of does not work for doing the same from background thread to background thread. It is best to create MyTask
a link to Queue
and poll the SwingWorker.doInBackground
queue for intermediate results.
a source to share
A1 + A2 . Yatendra, is it necessary that your main SwingWorker
be the only one that transfers intermediate results to EDT
? If your tasks were also instances SwingWorker
, the main worker could delegate the response to send intermediate results back EDT
to them and just take care of the lifecycle TaskWorkers
.
package threading;
import java.util.LinkedList;
import java.util.List;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
class MainSwingWorker extends SwingWorker<Void, Void> {
private List<TaskWorker> tasks;
public MainSwingWorker() {
tasks = new LinkedList<TaskWorker>();
for(int i=0; i<2; i++)
tasks.add(new TaskWorker(i));
}
@Override
public Void doInBackground() throws Exception {
Test.log("Building tasks.");
for(TaskWorker task : tasks)
launch(task);
Test.log("Waiting 5 secs.");
Thread.sleep(5000);
Test.log("Cancelling tasks");
for(TaskWorker task : tasks )
task.cancel(true);
return null;
}
private void launch(final TaskWorker task) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Test.log("Launching task worker.");
task.execute();
}
});
}
}
class TaskWorker extends SwingWorker<Void, String> {
private int id;
public TaskWorker(int wid) {
id = wid;
}
@Override
public Void doInBackground() throws Exception {
System.out.format("[%s] Starting worker %s\n", Thread.currentThread().getName(), id );
while( !isCancelled() ) {
// ***************************
// your task process code here
// ***************************
publish(String.format("A dummy interim result #%s", id));
Thread.sleep(1000);
}
return null;
}
@Override
public void process(List<String> results) {
// it pretty obvious, that once this method gets called you can safely
// call the Swing API from EDT among with the interim results
for(String result : results )
Test.log(result);
}
}
public class Test {
public static void log(String msg) {
System.out.format("[%s] %s\n", Thread.currentThread().getName(), msg);
}
public static void main(String[] args) throws Exception {
log("Init.");
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
log("Starting main worker.");
MainSwingWorker worker = new MainSwingWorker();
worker.execute();
}
});
Thread.sleep(7000);
log("Finished.");
}
}
Remember this is just a test, I know there are some ugly calls Thread.sleep(long)
.
[main] Init.
[AWT-EventQueue-0] Starting main worker.
[SwingWorker-pool-1-thread-1] Building tasks.
[SwingWorker-pool-1-thread-1] Waiting 5 secs.
[AWT-EventQueue-0] Launching task worker.
[AWT-EventQueue-0] Launching task worker.
[SwingWorker-pool-1-thread-2] Starting worker 0
[SwingWorker-pool-1-thread-3] Starting worker 1
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[SwingWorker-pool-1-thread-1] Cancelling tasks
[main] Finished.
A3
But if your project requires another one ExecutorService
to schedule its task, I would implement a similar publishing mechanism to perform the communication between your Main Swing Worker thread and that task. While this seems repetitive, can you use it java.concurrent.ConcurrentQueue
to store intermediate results as they appear?
PS: I just noticed a few days ago, but there is an annoying bug in SwingWorkers that prevents the ExecutorService from caching unused threads .
a source to share
SwingWorker is also Future. As such, it has a get () method that can be used inside the done () method to get the result of doInBackground () when that method ends.
Thus, the construction becomes something like this:
SwingWorker<T,P> sw=new SwingWorker<T,P>() {
@Override
public T doInBackground() throws Exception {
T result;
// do stuff here
return result;
}
@Override
public void done() {
try {
T result=get();
// do stuff with result.
}
catch(ExecutionException e) {
Exception fromDoInBackground= (Exception) e.getCause();
// handle exception thrown from doInBackground()
}
catch(InterruptedException i) {
// handle the case in which a SwingWorker was cancelled. typically: do nothing.
}
}
};
a source to share