gpt4 book ai didi

javafx 从具有可调用线程的结果

转载 作者:行者123 更新时间:2023-12-02 01:30:19 24 4
gpt4 key购买 nike

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

  • 等待()/通知()
  • 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();
}
}

我的等待任务是:

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(); 并且应用程序卡住了。我该如何修复它?

最佳答案

您做错了一些事情:

<小时/>
  1. 您将 Callable 包装在 FutureTask 中,然后将其提交给 ExecutorService。你不需要这样做,事实上你也不应该这样做。相反,只需直接提交您的 Callable,您就会得到 Future 作为返回。

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

    如果您使用 ExecutorService 的核心实现,则返回的 Future 无论如何都是 FutureTask。并不是说你应该关心——唯一重要的是它是一个 future 。请注意,Runnable 也是如此;只需直接提交它们,不要先将它们包装在 FutureTask 中。

<小时/>
  • 您正在提交 Callable,获取 Future,并将所述 Future 包装在 Task 中code>...然后提交您的任务。这意味着您要执行的每一项任务都将有两个 个任务。根据 ExecutorService 的配置方式,这相当于每个任务使用两个线程

    您应该像使用 Callable 一样使用您的 Task。在 Task#call() 方法中执行工作并返回结果。然后只提交 Task,不要先将其包装在任何内容中。

    executor.execute(task); // Don't need the Future here, just use "execute"

    如果您想要Task的结果,您可以注册回调(请参阅this)。该类旨在在 JavaFX 应用程序线程上调用这些回调。

    task.setOnSucceeded(event -> {
    T value = task.getValue();
    // do something with value...
    });

    请注意,Task 扩展了 FutureTask。这似乎与第 1 点矛盾,但事实就是如此。就我个人而言,我不会以这种方式设计该类——当使用执行器框架

  • <小时/>
  • 这与数字 2 有关;如果您解决了该问题,那么该问题就会自然消失。

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

  • <小时/>
  • 如果方法名称“showSpinner”和“hideSpinner”没有误导性,则您正在从后台线程更新 UI。 决不从 JavaFX 应用程序线程以外的线程更新 UI。现在,您可以将这些调用包装在Platform.runLater操作中,但您也可以使用Task的属性/回调。例如,您可以监听 running 属性来了解何时显示和隐藏微调器。
  • <小时/>

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

    // Doesn't have to be an anonymous class
    Task<String> task = new Task<>() {

    @Override
    protected String call() {
    System.out.println("test");
    return "test2";
    }

    });
    task.runningProperty().addListener((obs, wasRunning, isRunning) -> {
    if (isRunning) {
    showSpinner();
    } else {
    hideSpinner();
    }
    });
    task.setOnSucceeded(event -> {
    String a = task.getValue();
    // Do something with value.
    });

    executorService.execute(task);

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

    关于javafx 从具有可调用线程的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56185086/

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