javafx - 为什么 ExecutorService 不返回我的任务结果?

标签 javafx concurrency task future executorservice

更新:

我在 JavaFx 应用程序上有一个按钮,应该在用户输入电子邮件和密码后进行登录。

<Button fx:id="loginButton" layoutX="157.0" layoutY="254.0" mnemonicParsing="false" onAction="#login" prefHeight="30.0" prefWidth="172.0" text="Login" />

我在 JavaFX 应用程序上有一个 ExecutorService。我创建了一个搜索数据库的任务。它很好地执行了查询并获取了 UserInfo 对象,问题是 ExecutorService 没有将结果传递给主线程。这是执行程序服务返回 null 问题的主要代码:

public class LoginController {
    @FXML
    private Button loginButton;
    @FXML
    private Label afterLoginText;
    @FXML
    private TextField email;
    @FXML
    private PasswordField password;
    @FXML
    private Hyperlink hyperlink;
    @FXML
    private ProgressBar progressBar;
    private Navegador navegador;

    public void login(ActionEvent event) {
        afterLoginText.setText("Login in, please wait...");
        String emailText = email.getText();
        String passwordText = password.getText();
        DAOGeneric<UserInfo> dao = new DAOGeneric<>();
        LoginAtDataBaseTask loginAtDataBaseTask = new LoginAtDataBaseTask(dao, emailText, passwordText);
        progressBar.progressProperty().bind(loginAtDataBaseTask.progressProperty());
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future future = executorService.submit(loginAtDataBaseTask);
        loginAtDataBaseTask.setOnSucceeded(workerStateEvent -> {
            UserInfo userInfo;
            try {
                userInfo = (UserInfo) future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
            if(userInfo == null){
                System.out.println("User info is null");
            }
            else{
                try {
                    changeToMainScreen(event, userInfo);
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
                //Set premium level if user have one
                //Optional - show premium info
            }
        });
        executorService.shutdown();
    }
    public void changeToMainScreen(ActionEvent event, UserInfo userInfo) throws IOException {
        Stage stage = (Stage) ((Node)event.getSource()).getScene().getWindow();
        runMainScreen("/mainScreen.fxml",stage, userInfo);
    }

这是 LoginAtDataBaseTask 类的代码:

public class LoginAtDataBaseTask extends Task <UserInfo> {

    private static DAOGeneric<UserInfo> dao;

    private static String email;

    private static String password;
    public LoginAtDataBaseTask(DAOGeneric<UserInfo> dao, String email, String password) {
        this.dao = dao;
        this.email = email;
        this.password = password;
    }

    @Override
    protected UserInfo call() {
        return doLogin();
    }

    private UserInfo doLogin(){
        final int maxProgress = 100;
        List<UserInfo> usersList = dao.findByAnyParameter("email", email, UserInfo.class);
        if(usersList.size() == 1){
            updateProgress(99,maxProgress);
            UserInfo user1 = usersList.get(0);
            String passwordDecoded = DecoderMD5.StringToMD5(password);
            if(user1.getUserPassword().equals(passwordDecoded)){
//                afterLoginText.setText("Login Sucess!");
                return user1;
            }
            else{
//                afterLoginText.setText("Wrong Password!");
            }
        }
        else if(usersList.size()>1){
            //More than one user with same email on BD
//            afterLoginText.setText("Error code 1 - report to administrator");
        }
        else if(usersList.size()==0){
//            afterLoginText.setText("This email is not registered! Please register first!");
        }
        else{
            //Erro at DAO Search
//            afterLoginText.setText("Error code 2 - report to administrator");
        }
        return null;
    }
}

我尝试了多种方式进行转换,并首先使用 Future 来接收提交,然后在 future 对象上调用 get ,但似乎没有任何效果。我已经阅读了相关类的java文档,但我不太明白为什么我的对象保持为空。

更新:我设置了 setOnSucceeded 但 future.get 一直返回 null,并且主 javafx ui 一直卡住。我一直做错了什么以及我能做些什么来解决它?

最佳答案

为什么get()返回null

这与 Task 有关。从根本上来说,是 Runnable 的实现。它不是 Callable 的实现。因此,您调用 #submit(Runnable) 它返回 Future<?> 1,表示预计不会有结果。一个Runnable无法返回值。换句话说,调用#get()将始终返回 null在这种情况下。

但你真的应该打电话 #execute(Runnable) 当经过Task时到ExecutorService , 反正。没有理由拥有 Future代表 Task 状态的对象。这至少有两个原因:

  1. 调用Future#get()是一个阻塞调用Task的全部目的是将结果专门传送回JavaFX应用程序线程。并且您绝不能永远阻止该线程,因为这样做会导致 UI 无响应。

  2. A Task FutureTask 2,这意味着它已经是 Future 。如果您确实需要等待结果(不在 FX 线程上),那么您可以调用 #get()关于Task实例。


1. 它确实应该被定义为返回 Future<Void>

2. 请注意,标准 ExecutorService实现,ThreadPoolExecutor ,包含所有提交的 RunnableCallable FutureTask 中的对象,至少默认情况下是这样。


如何获取 Task 的结果

如前所述,Task 的目的(以及其他 javafx.concurrent 类)的作用是将工作卸载到后台线程,但将结果(以及消息、进度等)传回JavaFX 应用程序线程。特别是该线程,而不是任何其他线程。但您不能阻塞 FX 线程。这意味着观察任务是否完成,而不是等待任务完成。然后当它完成时,你通过做需要做的事情来使用react。

但是如何从已完成的 Task 中获取值?你查询其 value 属性,该属性将设置为 #call() 返回的任何内容。任务成功时的方法。如果需要,您可以使用监听器直接观察此属性。就我个人而言,我更喜欢使用 onSucceededonFailed特性。例如:

Task<SomeObject> task = ...;

task.setOnSucceeded(e -> {
    SomeObject result = task.getValue();
    // do something with 'result'
});

task.setOnFailed(e -> {
    task.getException().printStackTrace(); // or log it with a proper logging framework
    // notify user of failure
});

executor.execute(task);

注意 Task 的属性喜欢 message , progress , value等等保证仅由 FX 线程设置。还有onXXX处理程序也保证仅由 FX 线程调用。

参见Concurrency in JavaFX javafx.concurrent 文档以获取更多信息。

关于javafx - 为什么 ExecutorService 不返回我的任务结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75104843/

相关文章:

C# 任务返回输出

c# - 在导航之前取消等待方法的最佳方法?

表的 JavaFX 表轮廓

javafx - 为什么这个 "binding"代码在 JavaFX 中没有按预期工作?

python - 在超时中包装 asyncio.gather

c# - 单元测试是否捕获到异常

java - 如何将值绑定(bind)到 ObservableList 的大小?

javafx如何在辅助窗口关闭时保留数据

javascript - 在 Nashorn 中的特定上下文中执行函数

c# - 当必须锁定每个列表元素时,多个线程应该如何访问列表?