javafx从具有可调用

时间:2019-05-17 11:00:55

标签: java javafx java-threads jfoenix

我有一个Javafx应用程序,并且我想用“等待”功能来包围一些代码。因此我的代码可以是Runnable和Callable。问题是从Callabe获得结果。我尝试玩:

  • wait()/ notify()
  • Platform.runLater
  • 手动创建守护程序线程
  • 服务

在这里阅读了一些文章后,但这没有帮助。

我怎么称呼它:

            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();并且应用程序冻结。我该如何解决?

2 个答案:

答案 0 :(得分:2)

您做错了一些事情:


  1. 您将Callable包裹在FutureTask中,然后再提交给ExecutorService。您不需要执行此操作,实际上您不应该这样做。相反,只需直接提交Callable,您就会得到Future的回报。

    Future<T> future = executor.submit(callable);
    

    如果您使用的是ExecutorService的核心实现,则返回的Future 将是 FutureTask。并不是您应该在乎-唯一重要的是它是Future。注意Runnable也是如此;只需直接提交即可,不要先将它们包装在FutureTask中。


  1. 您要提交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)中>。


  1. 这与数字2有关;如果您解决了该问题,则此问题就自然消失了。

    您正在旋转,等待包装的Future完成。这是浪费资源。 FutureTask接口具有Future方法,该方法将阻塞调用线程,直到完成上述Future为止。如果get()正常完成,您将获得返回的值,否则,如果异常完成,则将抛出Future。第三种选择是调用线程被中断并抛出Future


  1. 如果方法名称“ ExecutionException”和“ InterruptedException”没有误导,则说明您正在从后台线程更新UI。 从不 JavaFX Application Thread 之外的其他线程更新UI。现在,您可以 将这些调用包装在showSpinner动作中,但是您也可以使用hideSpinner的属性/回调。例如,您可以收听Platform.runLater属性,以了解何时显示和隐藏微调框。

考虑到所有这些,您的示例应更像:

Task

有关更多信息,建议阅读:

答案 1 :(得分:0)

感谢所有人的帮助,尤其是@Slaw和@kendavidson 终于,我在这里找到了一个简单而完美的解决方案: Modal JaxaFX Progress Indicator running in Background

也许我会根据这个原则在这里发布我的完整的基于泛型的示例