简体   繁体   English

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

[英]Why is ExecutorService not returning the results of my Task?

UPDATE:更新:

I have a button on a JavaFx App that should do the login after user input email and password.我在 JavaFx 应用程序上有一个按钮,应该在用户输入 email 和密码后进行登录。

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

I have an ExecutorService on a JavaFX app.我在 JavaFX 应用程序上有一个 ExecutorService。 I created a task to search the DB.我创建了一个任务来搜索数据库。 It executes the query well and gets the UserInfo object, the problem is, the ExecutorService is not passing the results to the main thread.它很好地执行查询并获得 UserInfo object,问题是 ExecutorService 没有将结果传递给主线程。 This is the main code that is having a problem of null returned from the executor service:这是执行程序服务返回 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);
    }

And here is the code of the LoginAtDataBaseTask Class:这是 LoginAtDataBaseTask Class 的代码:

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;
    }
}

I tried casting on many ways and using Future first to receive the submit and then calling get on the future object, but nothing seems to work.我尝试了多种方式并首先使用 Future 来接收提交,然后在未来 object 上调用 get,但似乎没有任何效果。 I have already readed the java documents of this related classes but i don't really understand why my object keeps null.我已经阅读了相关课程的 java 文档,但我真的不明白为什么我的 object 保留 null。

UPDATE: I put a setOnSucceeded but the future.get keeps returning null, and the main javafx ui keeps freezing.更新:我设置了 setOnSucceeded 但 future.get 一直返回 null,而主 javafx ui 一直冻结。 What am i keeping doing wrong and what can i do to solve it?我一直做错了什么,我能做些什么来解决它?

Why does get() return null ?为什么get()返回null

This has to do with the fact that Task is, fundamentally, an implementation of Runnable .这与Task从根本上说是Runnable的实现这一事实有关。 It is not an implementation of Callable .它不是Callable的实现。 Therefore, you are calling #submit(Runnable) which returns a Future<?> 1 , meaning no result is expected.因此,您正在调用#submit(Runnable)返回Future<?> 1 ,这意味着没有预期结果。 A Runnable cannot return a value. Runnable不能返回值。 In other words, the call to #get() will always return null in this case.换句话说,在这种情况下,对#get()的调用将始终返回null

But you should really be calling #execute(Runnable) when passing a Task to an ExecutorService , anyway.但是无论如何,在将Task传递给ExecutorService时,您真的应该调用#execute(Runnable) There's no reason to have a Future object representing the status of the Task .没有理由让Future object 代表Task的状态。 This is for at least two reasons:这至少有两个原因:

  1. The call to Future#get() is a blocking call .Future#get()的调用是阻塞调用 The whole purpose of Task is to communincate a result specifically back to the JavaFX Application Thread . Task的全部目的是专门将结果传达给JavaFX Application Thread And you must never block that thread, as doing so will lead to an unresponsive UI.而且您绝不能阻塞该线程,因为这样做会导致 UI 无响应。

  2. A Task is a FutureTask 2 , which means it is already a Future .一个Task一个FutureTask 2 ,这意味着它已经是一个Future If you really need to wait for a result ( not on the FX thread ), then you can just call #get() on the Task instance.如果您确实需要等待结果(而不是在 FX 线程上),那么您可以在Task实例上调用#get()


1. It really should have been defined to return Future<Void> 1.确实应该定义返回Future<Void>

2. Note that the standard ExecutorService implementation, ThreadPoolExecutor , wraps all submitted Runnable and Callable objects in a FutureTask , at least by default. 2.请注意,标准的ExecutorService实现ThreadPoolExecutor将所有提交的RunnableCallable对象包装在FutureTask中,至少在默认情况下是这样。


How to get the result of a Task如何获得Task的结果

As noted earlier, the purpose of Task (and the other javafx.concurrent classes) is to offload work to a background thread but communicate a result (as well as messages, progress, etc.) back to the JavaFX Application Thread .如前所述, Task (和其他javafx.concurrent类)的目的是将工作卸载到后台线程,但将结果(以及消息、进度等)传回JavaFX 应用程序线程 That thread specifically, not any other thread.特别是那个线程,而不是任何其他线程。 But you must not block the FX thread.但是你不能阻塞 FX 线程。 That means observing the task for completion, not waiting for it to complete.这意味着要观察任务是否完成,而不是等待它完成。 Then when it does complete, you react by doing what needs doing.然后当它完成时,您通过做需要做的事情来做出反应。

But how to get the value from a completed Task ?但是如何从已完成的Task中获取价值呢? You query its value property, which will be set to whatever is returned by the #call() method if and when the task succeeds.您查询它的value属性,如果任务成功,该属性将被设置为#call()方法返回的任何值。 You can directly observe this property with a listener, if you want.如果需要,您可以使用侦听器直接观察此属性。 Personally, I prefer using the onSucceeded and onFailed properties.就个人而言,我更喜欢使用onSucceededonFailed属性。 For example:例如:

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);

Note properties of Task like message , progress , value and so on are guaranteed to only be set by the FX thread.注意Task的属性,如messageprogressvalue等保证只能由 FX 线程设置。 And the onXXX handlers are also guaranteed to be invoked only by the FX thread. onXXX处理程序也保证仅由 FX 线程调用。

See Concurrency in JavaFX and javafx.concurrent documentation for more information.有关详细信息,请参阅JavaFXjavafx.concurrent文档中的并发。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM