在单独的线程中运行进程以使Java FX应用程序的其余部分可用?

时间:2017-06-06 19:46:45

标签: java multithreading javafx

我目前正在创建一个使用Java FX创建的UI的Java应用程序。我的应用程序界面分为三个单独的选项卡。在第一个选项卡上,我有很多按钮可以启动其他应用程序。我的目标是让用户以特定顺序启动这些应用程序,因此在前一个应用程序的过程完成之前,后续按钮被禁用,然后启用它们。我已经通过使用行

完成了这项工作
Process p = Runtime.getRuntime().exec(application);
p.waitFor();

然后控制器启用界面中的下一个按钮。启动的应用程序运行时出现问题。由于我的应用程序等待进程结束,任务管理器显示我的Java应用程序没有响应,并且您无法导航到其他选项卡。当然,当启动的应用程序完成后,我的应用程序正常运行。

我认为这是由于主要线程被阻止,因为它正在等待一个进程完成。最好的解决方案是在单独的线程上运行此进程,以便在等待已启动的进程完成时,应用程序的其余部分是否可用?

1 个答案:

答案 0 :(得分:5)

  

我认为这是由于主要线程被阻止,因为它正在等待一个进程完成。

这是正确的。

  

是否可以在单独的线程上运行此进程,以便在等待已启动的进程完成时可以使用其余应用程序?

是的,当然。最简单的方法就是在一个单独的线程上启动它:

Process p = Runtime.getRuntime().exec(application);
new Thread(p::waitFor).start();

但是,你可能还需要更多:在新线程完成时收到通知等.JavaFX提供了concurrency API,它对您执行的任务的生命周期进行了回调。这些回调在FX应用程序线程上执行,因此更新回调中的UI是安全的(您无法从后台线程更新UI)。

所以你可以做点什么

Task<Void> executeAppTask = new Task<Void>() {
    @Override
    protected Void call() throws Exception {
        Process p = Runtime.getRuntime().exec(application);
        p.waitFor();
    }
};

executeAppTask.setOnSucceeded(e -> {
    /* code to execute when task completes normally */
});

executeAppTask.setOnFailed(e -> {
    Throwable problem = executeAppTask.getException();
    /* code to execute if task throws exception */
});

executeAppTask.setOnCancelled(e -> {
    /* task was cancelled */
});

Thread thread = new Thread(executeAppTask);
thread.start();

请注意,setOnSucceeded()setOnFailed()setOnCancelled()处理程序在FX应用程序线程上执行,因此,再次更新这些处理程序中的UI是安全的。另请注意,如果需要,调用executeAppTask.cancel()将中断运行任务的线程,这将正确中断waitFor()方法。因此,您将(或多或少)通过调用setOnCancelled立即调用executeAppTask.cancel()处理程序。如果在调用阻塞方法(例如call())后执行了<{1}}方法中的代码,它应该可以捕获中断的异常并检查{{1} }如果任务已被取消,则正常退出。

有关更多示例,请参阅Task API docs