Swing 是单线程环境,它也不是线程安全的。
这意味着任何长时间运行或阻塞的进程都不应该在事件调度线程的上下文中运行,因为它会阻止它处理新事件,包括重绘请求,使程序看起来像挂起一样。
这也意味着你不应该从 EDT 的上下文之外创建或修改任何 UI 组件的状态。
更多详情请见Concurrency in Swing。
您可以使用Thread 在其中运行流程,但您将负责确保您想要对 UI 进行的任何更改都在 EDT 的上下文中手动执行。这可能会变得很麻烦,尤其是当您想将信息从线程传递到 EDT 时。
另一个解决方案是使用SwingWorker,它提供的功能可以更轻松地在其后台线程和 EDT 之间同步数据。它支持通过setProgress 方法和PropertyListener 支持的进度通知,以及在EDT 上下文中从后台线程和process 获取publish 数据的能力。它还有一个不错的 done 方法,可以让您知道后台线程何时完成,但它是在 EDT 的上下文中执行的,例如...
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;
}
}
现在,您可能很想将JProgressBar 传递给SwingWorker 并在done 方法中重置其状态,但这确实让它有点悬而未决,不知道工人何时真正开始,当然,你应该在调用worker之前设置状态,但是,管理UI的状态不是worker的责任
相反,您可以利用工作人员 PropertyChangeListener 的支持,例如...
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();
这使您可以决定如何响应工作人员,而无需将您与特定的工作流程联系起来。
见Worker Threads and SwingWorker