我继续阅读并研究实践书中的并发性,我无法理解第9.3.2章中的例子。
本章介绍GUI应用程序的开发。
在文本中,作者说,如果事件处理程序是长时间运行的任务-您可以在分离的线程中运行它,以提高应用程序的响应性。
作者在书中提供了以下代码:
BackgroundTask:
abstract class BackgroundTask<V> implements Runnable, Future<V> {
private final FutureTask<V> computation = new Computation();
private class Computation extends FutureTask<V> {
public Computation() {
super(new Callable<V>() {
public V call() throws Exception {
return BackgroundTask.this.compute();
}
});
}
protected final void done() {
GuiExecutor.instance().execute(new Runnable() {
public void run() {
V value = null;
Throwable thrown = null;
boolean cancelled = false;
try {
value = get();
} catch (ExecutionException e) {
thrown = e.getCause();
} catch (CancellationException e) {
cancelled = true;
} catch (InterruptedException consumed) {
} finally {
onCompletion(value, thrown, cancelled);
}
}
;
});
}
}
protected void setProgress(final int current, final int max) {
GuiExecutor.instance().execute(new Runnable() {
public void run() {
onProgress(current, max);
}
});
}
// Called in the background thread
protected abstract V compute() throws Exception;
// Called in the event thread
protected void onCompletion(V result, Throwable exception,
boolean cancelled) {
}
protected void onProgress(int current, int max) {
}
// Other Future methods forwarded to computation
}和runInBackground方法:
public void runInBackground(final Runnable task) {
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
class CancelListener implements ActionListener {
BackgroundTask<?> task;
public void actionPerformed(ActionEvent event) {
if (task != null)
task.cancel(true);
}
}
final CancelListener listener = new CancelListener();
listener.task = new BackgroundTask<Void>() {
public Void compute() {
while (moreWork() && !isCancelled())
doSomeWork();
return null;
}
public void onCompletion(boolean cancelled, String s,
Throwable exception) {
cancelButton.removeActionListener(listener);
label.setText("done");
}
};
cancelButton.addActionListener(listener);
backgroundExec.execute(task);
}
});
} 我无法理解这段代码的逻辑。根据代码,我看到runInBackground方法将侦听器添加到startButton,后者在分离的(非Swing)线程中运行任务(作为runInBackground方法的参数传递)。
但是这个方法的其他代码对我来说不太清楚。根据书中的课文,我们应该有可能从Swing线程中断这项任务。但是在方法文本中,我们向cancelButton中添加了额外的侦听器,这使得工作与作为runInBackground方法参数传递的停止任务无关。
请澄清这件事。
发布于 2017-03-04 13:22:40
这本书有errata列表
在清单9.8中,backgroundExec.execute的参数应该是listener.task,而不仅仅是任务,清单的第一行和最后一行应该被删除。
因此,我的关切是对的。
发布于 2017-03-02 17:44:23
Swing本质上是单线程的。如果一个长时间运行的任务在事件调度线程(EDT)上排队,它将阻塞直到完成,并阻止进一步的事件处理。如果EDT被阻塞,这将使GUI看起来“冻结”(至少在队列处理恢复之前)。
为了避免这种情况,将非GUI工作放在其他线程上是一个很好的实践。通常,这些线程的创建是由GUI事件触发的,线程通过回调来表示它们的完成,因此可以根据需要更新GUI。
在本例中,CancelButton上的按钮按下事件确实起源于EDT。但是,按钮按下的事件处理程序必须保留对BackgroundTask的引用,以便它能够调用cancel(true)方法。因此,当用户单击startButton时,会出现以下顺序:
ActionListner for startButton是由用户按下按钮触发的。BackgroundTask。BackgroundTask相关的新的BackgroundTask附加到cancelButtonBackgroundTask已经启动。如果用户单击cancel按钮,附加的ActionLister将调用BackgroundTask上的cancel()。
否则,一旦BackgroundTask完成,它就会删除CancelListener,因为取消已完成的任务是没有意义的。
有关EDT如何工作的更多信息,您可能需要查看以下问题:Java事件-分派线程解释
正如注释中指出的那样,编辑,Swing组件通常不是线程安全的,它是从EDT修改。
乍一看,onCompletion()的BackgroundTask方法似乎是从工作线程而不是从EDT修改cancelButton和label。
但是,GuiExecutor提供了一个execute()方法,以确保传入的Runnable在EDT上运行:
public void execute(Runnable r) {
if (SwingUtilities.isEventDispatchThread())
r.run();
else
SwingUtilities.invokeLater(r);
}BackgroundTask使用execute()方法调用其onCompletion()方法。因此,对cancelButton和label的修改是从EDT运行的。
由于runInBackground也在操作Swing组件,所以也应该从EDT调用它。
https://stackoverflow.com/questions/42559937
复制相似问题