如何在 Java 中执行进程而不导致程序冻结? 我尝试过使用 SwingWorker,但我还不太明白它是如何工作的。
还有其他方法可以实现这个目标吗?
提前致谢!
编辑:
感谢您的回答。但我有一个包含多种方法的类(嗯,它不止一个类,但你明白我的意思);我如何使用 SwingWorker 与它们交互?
这是其中一门课程:
/**
*
* @author Simon
*/
public abstract class Command extends SwingWorker {
BufferedReader prReader = null;
ProcessBuilder process = null;
Process pr = null;
Date timeNow = new Date();
String osName = System.getProperty("os.name");
public void executeProcessNoReturn(String _process, String arg) throws IOException {
process = new ProcessBuilder(_process, arg);
pr = process.start();
}
public String executeProcessReturnLastLine(String _process, String arg) throws IOException {
process = new ProcessBuilder(_process, arg);
pr = process.start();
prReader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line;
while ((line = prReader.readLine()) != null) {
// Wait for input to end.
}
return line;
}
public StringBuilder executeProcessReturnAllOutput(String _process, String arg) throws IOException {
process = new ProcessBuilder(_process, arg);
pr = process.start();
prReader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
StringBuilder output = null;
String line;
while ((line = prReader.readLine()) != null) {
output.append(line);
}
return output;
}
public boolean isProcessRunning(String processName) throws IOException {
boolean value = false;
if (osName.equals("Linux") | osName.contains("Mac")) {
process = new ProcessBuilder("ps", "-e");
pr = process.start();
String line;
prReader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
while ((line = prReader.readLine()) != null) {
if (line.contains(processName)) { value = true; break; }
}
} else {
String winDir = System.getenv("windir") + "/System32/tasklist.exe";
process = new ProcessBuilder(winDir);
pr = process.start();
String line;
prReader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
while ((line = prReader.readLine()) != null) {
if (line.contains(processName)) { value = true; break; }
}
}
return value;
}
public String executeProcessReturnError(String processName, String arg) throws IOException {
process = new ProcessBuilder(processName, arg);
process.redirectError();
pr = process.start();
prReader = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
String line;
String output = "";
while ((line = prReader.readLine()) != null) {
output += line + "\n";
}
return output;
}
}
是的,你可以使用
SwingWorker
,这个想法是,一个需要花费大量时间在单独的线程(后台线程)中运行的任务,然后你不会阻塞你的gui,你的JFrame也不会冻结。这是一个完整的示例,我非常喜欢 Swing Worker 示例。
基本上作为一个例子,您创建自己的类来扩展
SwingWorker
覆盖 doInBackground
。
注意: 您可以像普通班级一样拥有字段。
示例:
class Worker extends SwingWorker<Void, String> {
private SomeClass businessDelegate;
private JLabel label;
@Override
protected Void doInBackground() throws Exception {
//here you make heavy task this is running in another thread not in EDT
businessDelegate.callSomeService();
setProgress(30); // this is if you want to use with a progressBar
businessDelegate.saveToSomeDataBase();
publish("Processes where saved");
return null;
}
@Override
protected void process(List<String> chunks){
//this is executed in EDT you can update a label for example
label.setText(chunks.toString());
}
//add setters for label and businessDelegate
}
您还阅读了有关
process(..)
publish(..)
和 done()
的内容。
然后将其放入您的客户端代码中。
SwingWorker<Void,String> myWorker = new Worker();
myWorker.execute();
您需要在单独的线程中运行代码。运行和协调程序中多条代码路径的关键词是“并发”:
http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html
多线程代码可能会变得相当棘手且难以调试,但 Java 提供了一些更高级别的抽象/概念,使得使用多线程代码不太容易出错。
查看 java.util.concurrent 包了解这些概念。
由于多线程是您在使用 AWT/Swing 时必然会遇到的一个问题,因此有一些实用程序类/方法专门设计用于促进图形应用程序中的多线程。 SwingWorker 是其中之一(另一个例如 SwingUtilities.invokeLater())。熟悉它们是个好主意,它们可以让你的生活更轻松,特别是对于足够长的简单任务,否则 GUI 就会冻结。
如果您使用 Swing,则可以使用 SwingWorker。
既然你提到你不明白它是如何工作的,我就简单解释一下。
Swing 事件处理代码是在事件调度线程中完成的。因此,如果用户单击按钮(例如更新按钮,该按钮从服务器获取数据并将其显示在窗口中),则在事件调度线程中调用该按钮的 ActionListener。
如果您在同一线程中获取数据,则 UI 将冻结,因为它无法处理任何事件(您正在事件调度线程中运行代码)。
所以你应该在单独的线程中获取数据。但获取数据后,您应该将其设置到事件调度线程中的视图。
Swing 工作线程让这一切变得简单。您将繁重任务放入
doInBackground
方法中(在我们的例子中是获取数据),并从该方法返回繁重任务的输出(在我们的例子中是数据)。
在 Swing Worker 的
done
方法中,您调用 get
方法,该方法将为您提供 doInBackground
方法中返回的数据。在那里,您可以使用新数据更新视图(例如将数据添加到 TableModel)。
当您现在在 Swing Worker 上调用
exectue
时,它将负责在单独的线程(不是事件调度线程)中运行 doInBackground
,并且它将在事件调度线程中运行 done
方法.
因此,通过使用 SwingWorker,您不必担心 EDT 中处理 UI 事件的内部结构以及另一个线程中的耗时任务。