我有一个Javafx应用程序,并且我想用“等待”功能来包围一些代码。因此我的代码可以是Runnable和Callable。问题是从Callabe获得结果。我尝试玩:
在这里阅读了一些文章后,但这没有帮助。
我怎么称呼它:
final String a =
CommonHelper.showWaiting(() -> {
System.out.println("test");
return "test2";
});
这就是我使用Runnable的方式:
public static void showWaiting(Runnable runnable) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
try {
executorService.submit(new WaitingTask<>(executorService.submit(runnable)));
} finally {
executorService.shutdown();
}
}
我的WaitingTask是:
public class WaitingTask<T> extends Task<Void> {
@Getter
private final Future<T> future;
public WaitingTask(Future<T> future) {
this.future = future;
}
@Override
protected Void call() {
showSpinner();
while (true) {
if (future.isDone()) {
hideSpinner();
break;
}
}
}
return null;
}
}
这太棒了-我的应用程序显示了正在等待的微调器,并且任务运行在单独的线程中。
因此,我尝试使用Callable以相同的方式获得结果:
public static <T> T showWaiting(Callable<T> callable) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
try {
FutureTask<T> task = new FutureTask<>(callable);
Future<T> result = (Future<T>) executorService.submit(task);
executorService.submit(new WaitingTask<>(result));
return result.get();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
executorService.shutdown();
}
}
但是我看不到正在等待微调器,也许应用程序的主线程正在等待result.get();
并且应用程序冻结。我该如何解决?
答案 0 :(得分:2)
您做错了一些事情:
您将Callable
包裹在FutureTask
中,然后再提交给ExecutorService
。您不需要执行此操作,实际上您不应该这样做。相反,只需直接提交Callable
,您就会得到Future
的回报。
Future<T> future = executor.submit(callable);
如果您使用的是ExecutorService
的核心实现,则返回的Future
将是 FutureTask
。并不是您应该在乎-唯一重要的是它是Future
。注意Runnable
也是如此;只需直接提交即可,不要先将它们包装在FutureTask
中。
您要提交Callable
,获取Future
,然后将Future
包裹在Task
中...然后提交{{1 }}。这意味着您要执行的每个任务都有两个 个任务。根据{{1}}的配置方式,这等同于每个任务使用两个线程。
您应该像使用Task
一样使用ExecutorService
。在Task
方法内进行工作并返回结果。然后只提交Callable
,不要先将其包装起来。
Task#call()
如果您希望获得Task
的结果,则可以注册回调(请参见this)。该类旨在在 JavaFX Application Thread 上调用这些回调。
executor.execute(task); // Don't need the Future here, just use "execute"
请注意,Task
扩展了task.setOnSucceeded(event -> {
T value = task.getValue();
// do something with value...
});
。这似乎与第1点相矛盾,但这就是事实。就我个人而言,我不会以这种方式设计该类—当使用 Executor Framework Task包裹在另一个FutureTask
(可能是Task
)中>。
这与数字2有关;如果您解决了该问题,则此问题就自然消失了。
您正在旋转,等待包装的Future
完成。这是浪费资源。 FutureTask
接口具有Future
方法,该方法将阻塞调用线程,直到完成上述Future
为止。如果get()
正常完成,您将获得返回的值,否则,如果异常完成,则将抛出Future
。第三种选择是调用线程被中断并抛出Future
。
ExecutionException
”和“ InterruptedException
”没有误导,则说明您正在从后台线程更新UI。 从不从 JavaFX Application Thread 之外的其他线程更新UI。现在,您可以 将这些调用包装在showSpinner
动作中,但是您也可以使用hideSpinner
的属性/回调。例如,您可以收听Platform.runLater
属性,以了解何时显示和隐藏微调框。考虑到所有这些,您的示例应更像:
Task
有关更多信息,建议阅读:
javafx.concurrent.Worker
的文档javafx.concurrent.Task
(以及running
的其他实现)的文档答案 1 :(得分:0)
感谢所有人的帮助,尤其是@Slaw和@kendavidson 终于,我在这里找到了一个简单而完美的解决方案: Modal JaxaFX Progress Indicator running in Background
也许我会根据这个原则在这里发布我的完整的基于泛型的示例