gpt4 book ai didi

java - Concurrency runInBackground方法逻辑实战[9.3.2]

转载 作者:行者123 更新时间:2023-11-29 04:34:01 25 4
gpt4 key购买 nike

我继续阅读和研究练习书中的并发性,但我无法理解第 9.3.2 章中的示例。

本章介绍GUI应用程序开发。

作者在文中说,如果事件处理程序是长时间运行的任务 - 您可以在单独的线程中运行它以提高应用程序的响应能力。

作者在书中提供了以下代码:

后台任务:

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 方法参数传递。

请澄清一下。

最佳答案

Swing 本质上是单线程的。如果长时间运行的任务在 Event Dispatch Thread (EDT) 上排队,它将阻塞直到完成并阻止进一步的事件处理。如果 EDT 被阻塞,这将使 GUI 看起来“卡住”(至少在队列处理恢复之前)。

为避免这种情况,将非 GUI 工作放在其他线程上是一个很好的做法。通常,这些线程的创建是由 GUI 事件触发的,线程通过回调发出完成信号,因此可以根据需要更新 GUI。

在此示例中,CancelButton 上的按钮按下事件 确实 源自 EDT。但是,按下按钮的事件处理程序必须保留对 BackgroundTask 的引用,以便它可以调用 cancel(true) 方法。因此,当用户单击 startButton 时,会发生以下序列:

  1. ActionListner for startButton 由用户按下按钮触发。
  2. BackgroundTask 已创建。
  3. 一个引用了 BackgroundTask 的新 ActionLister 附加到 cancelButton
  4. BackgroundTask 已启动。

如果用户点击取消按钮,附加的ActionLister 将在BackgroundTask 上调用cancel()

否则,一旦 BackgroundTask 完成,它就会删除 CancelListener,因为取消已完成的任务将毫无意义。

有关 EDT 工作原理的更多信息,您可能需要查看以下问题:Java Event-Dispatching Thread explanation

EDIT 正如评论中指出的那样,Swing 组件通常不是线程安全的,它是 recommended they only be modified from the EDT .

乍一看,BackgroundTaskonCompletion() 方法似乎同时修改了 cancelButtonlabel 来自工作线程而不是 EDT。

然而,GuiExecutor提供一个 execute() 方法,确保传入的 Runnable 在 EDT 上运行:

public void execute(Runnable r) {
if (SwingUtilities.isEventDispatchThread())
r.run();
else
SwingUtilities.invokeLater(r);
}

BackgroundTask 使用 execute() 方法调用其 onCompletion() 方法。因此,从 EDT 运行对 cancelButtonlabel 的修改。

因为 runInBackground 它也在操纵 Swing 组件,所以它也应该从 EDT 调用。

关于java - Concurrency runInBackground方法逻辑实战[9.3.2],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42559937/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com