You should have a look at Worker Threads and SwingWorker and the JavaDocs for SwingWorker
which will give you more information.
Basically, publish
places the values onto a queue which will be passed to the publish
method within the context of the EDT. This is used to allow information to be passed from the background thread of the worker to the EDT where the values can be used to update the UI safely.
The done
method should be used to get the result of the background operation (assuming it has one), which can retrieved by using the get
method which will return the value which was returned from the doInBackground
method or the exception which was generated
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingWorker;
public class Test {
public static void main(String[] args) throws InterruptedException {
ExampleWorker worker = new ExampleWorker();
worker.execute();
Thread.sleep(100);
try {
System.out.println(">> " + worker.get());
} catch (ExecutionException ex) {
ex.printStackTrace();
}
}
public static class ExampleWorker extends SwingWorker<String, Integer> {
private int n = 0;
private long startTime;
@Override
protected String doInBackground() throws Exception {
System.out.println("doInBackground()");
startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < 3000) {
publish(n++);
Thread.yield();
}
return "What is this used for?";
}
@Override
protected void process(List<Integer> chunks) {
for (int value : chunks) {
System.out.println(value);
}
}
@Override
protected void done() {
System.out.println("Done: " + n);
}
}
}
Updated with a more directed example...
I don't have a process method because I don't want to pass information to the EDT until the background operation finishes.
Then don't use publish
/process
, use done
and return the value you want to be retrieved from doInBackground
The only reason I still have publish in my code is because it doesn't work without it, if I don't cancel the process.
Seems to work okay for me, and SwingWorker
already has it owns cancel state
And doesn't get() block everything on the EDT? I don't want that because it prevents my GUI from working
Yes, while the worker is running, it will return immediately if the worker has completed. In your example, with the absence of any UI to keep the JVM from exiting, it was a way to keep the JVM from exiting
This means you can use the done
which is normally called after doInBackground
returns and/or within a PropertyChangeListener
, depending on your needs
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) throws InterruptedException {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JLabel("Waiting"));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
ExampleWorker worker = new ExampleWorker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
ExampleWorker worker = (ExampleWorker) evt.getSource();
switch (worker.getState()) {
case DONE: {
try {
System.out.println("PropertyChange: " + worker.get());
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
frame.dispose();
}
break;
}
}
}
});
worker.execute();
}
});
}
public static class ExampleWorker extends SwingWorker<Integer, Integer> {
private int n = 0;
private long startTime;
@Override
protected Integer doInBackground() throws Exception {
System.out.println("doInBackground()");
startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < 3000) {
n++;
Thread.yield();
}
return n;
}
@Override
protected void process(List<Integer> chunks) {
for (int value : chunks) {
System.out.println(value);
}
}
@Override
protected void done() {
try {
System.out.println("Done: " + get());
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}
}
}