Swing is a single threaded environment, it is also not thread safe.
This means that any long running or blocking process should never be run within the context of the Event Dispatching Thread, as it will prevent it from processing new events, including repaint requests, making the program look like it's hung.
It also means that you should NEVER create or modify the state of ANY UI component from outside the context of the EDT.
See Concurrency in Swing for more details.
You can use a Thread
to run the process within, but you will become responsible for ensure that any changes you want to make to the UI are carried out within the context of the EDT manually. This can become troublesome especially when you want to pass information from the thread to the EDT.
Another solution is to use a SwingWorker
which provides functionality that can synchronise data between it's background thread and the EDT more easily. It supports progress notification via the setProgress
method and PropertyListener
support as well as the ability to publish
data from the background thread and process
within the context of the EDT. It also has a nice done
method which lets you know when the background thread has completed, but which is executed within the context of the EDT, for example...
public static class FormatWorker extends SwingWorker<Integer, Integer> {
private String drive;
public FormatWorker(String drive) {
this.drive = drive;
}
@Override
protected Integer doInBackground() throws Exception {
String[] command = {"CMD", "/C", "MyCmdCommand"};
ProcessBuilder probuilder = new ProcessBuilder(command);
probuilder.directory(new File(drive + ":\\"));
Process process = probuilder.start();
return process.waitFor();
return 0;
}
}
Now, you might be tempted to pass in the JProgressBar
to the SwingWorker
and reset its state within the done
method, but this does leave it a hanging a little, not knowing when the worker has actually started, sure, you should set the state before you call the worker, but still, it's not the responsibility of the worker to manage the state of the UI
Instead, you could take advantage of the workers PropertyChangeListener
support, for example...
PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println(evt.getPropertyName() + "; " + evt.getNewValue());
if ("state".equals(evt.getPropertyName())) {
SwingWorker.StateValue state = (SwingWorker.StateValue) evt.getNewValue();
switch (state) {
case DONE:
try {
int exitLevel = ((SwingWorker<Integer, ?>)evt.getSource()).get();
JOptionPane.showMessageDialog(null, "Format command completed with exit level of " + exitLevel);
} catch (InterruptedException | ExecutionException ex) {
JOptionPane.showMessageDialog(progressBar, ex.getMessage());
} finally {
progressBar.setIndeterminate(true);
}
break;
case STARTED:
progressBar.setIndeterminate(true);
break;
}
}
}
};
FormatWorker worker = new FormatWorker("G");
worker.addPropertyChangeListener(listener);
worker.execute();
This allows you to decide how you want to respond to the worker, without coupling you to a particular work flow.
See Worker Threads and SwingWorker