我创建了一个有 3 个按钮的 GUI;连接、断开、中止。它们都共享一个公共(public) SwingWorker 线程,但由于某种原因,除非我按下中止按钮,否则该线程永远不会完成。
我还没有为断开连接按钮编写任何代码,但即便如此,它与我的问题无关。但是,为什么线程不直接完成并打印 Done
陈述?我的猜测是它陷入了 this.isCancelled()
循环,但即使我这样做:
while(!this.isCancelled() || !this.isDone()) { // run loop }
它从不打印Done
只有在我按下中止按钮后,线程才会完成。我正在使用isCancelled()
循环方法,因为这似乎是取消 SwingWorker 的推荐方法。
我只发布下面的相关代码,因为实际上有数百行。
private class ButtonListener implements ActionListener {
private MyWorker worker;
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(connectButton)) {
worker = new MyWorker(activityTextArea);
worker.execute();
activityTextArea.append("Connect Button Pressed");
}
if(e.getSource().equals(disconnectButton)) {
activityTextArea.append("Disconnect Button Pressed");
}
if(e.getSource().equals(abortButton)) {
worker.cancel(true);
activityTextArea.append("Abort Button Pressed");
}
}
}
这是 SwingWorker:
public class MyWorker extends SwingWorker<Void, Void> {
private JTextArea textArea;
MyWorker(JTextArea textArea) {
this.textArea = textArea;
}
private void startWorker() {
ProcessBuilder pb = new ProcessBuilder();
pb.command("aCmd");
Process p;
try {
p = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while((line = br.readLine()) != null) {
textArea.append(line + "\n");
}
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
protected Void doInBackground() throws Exception {
// TODO Auto-generated method stub
boolean isFirstTime = true;
while(!this.isCancelled()) {
if(this.isDone()) { break; }
Thread.sleep(3000);
if(isFirstTime) {
startWorker();
isFirstTime = false;
}
}
return null;
}
@Override
protected void done() {
textArea.append("Done\n");
}
}
最佳答案
这里的问题在于 MyWorker.doInBackground() 方法的实现, isDone() 用于控制循环的执行。这可能不是您希望 doInBackground() 工作的方式。
要真正了解发生了什么,我们需要考虑 isDone() 的行为和 SwingWorker 的生命周期。
isDone() 方法在以下情况下返回 true:
- done() 执行完成后
- 该工作人员已被取消
- 由于某些异常而异常终止。
SwingWorker 生命周期
- PENDING:从对象构造到调用 doInBackground 之前期间的状态。
- STARTED:从调用
doInBackground()
之前不久到调用done()
之前不久期间的状态。 SwingWorker 在进入此状态之前会被分配一个工作线程,并且该工作线程执行 doInBackground()。- 在此状态下执行
doInBackground()
时,worker 可以使用publish(..)
发送中间结果,然后获取process(.. )
由事件调度线程 (EDT) 实现。
- 在此状态下执行
- DONE:在事件调度线程 (EDT) 中的
done()
完成执行后,会转换到此状态。这是对象剩余存在的最终状态。
为了编写高性能且无故障的 GUI,了解上述工作流程非常重要。
根据上述讨论,您可能希望替换 if(this.isDone()) { break; }
包含反射(reflect)您预期处理完成情况的内容。
这里的另一个问题是,您正在由 doInBackground()
调用的方法中执行与 swing 相关的 Activity 。 textArea.append(line + "\n");
应该发生在事件调度线程中执行的 process(..) 方法中,如下所示:
public class MyWorker extends SwingWorker<Void, String> {
@Override
protected void process(List<String> chunks) {
for (String line : chunks) {
textArea.append(line + "\n"); // display intermediate results from publish()
}
}
private void startWorker() throws IOException {
ProcessBuilder pb = new ProcessBuilder("aCmd");
BufferedReader br = null;
try {
Process p = pb.start();
br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
publish(line); // publish a chunk of result to process(..)
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) br.close();
}
}
引用:http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html
关于java - 使用 isCancelled() 时 SwingWorker 不会停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25345105/