【问题标题】:JavaFX task waiting for another task to finishJavaFX 任务等待另一个任务完成
【发布时间】:2018-02-04 13:00:22
【问题描述】:

我对并发还很陌生,我已经多次碰壁了。 代码几乎描述了所有内容,但只是为了澄清:用户按下按钮,应用程序将查询发送到数据库,同时 statusLabel 设置为:
非常好。
200 毫秒
非常好..
200 毫秒
非常好...
200 毫秒
查询结果

我已经做到了,但是现在,我需要在另一个类中使用查询结果(如果成功,则打开另一个窗口),但它永远不会。我得出的结论是,它只是在任务完成之前检查结果,所以结果总是错误的,我不知道如何解决这个问题,所以另一个类在任务完成后检查条件。

首先,我的Authorization 班级

public class Authorization {
    private static String query = "";
    private static boolean isValid;
    private static Task<Void> task;

    public static void verifyLogin(String username, String password) throws SQLException{
        Status.get().unbind();
        isValid = false;
        task = new Task<Void>() {
            @Override
            protected Void call() throws SQLException {
               while(!isCancelled()) {
                   try {
                       updateMessage("Weryfikacja.");
                       Thread.sleep(200);
                       updateMessage("Weryfikacja..");
                       Thread.sleep(200);
                       updateMessage("Weryfikacja...");
                       Thread.sleep(200);

                       if(username.equals("") || password.equals("")) {
                           task.cancel();
                           updateMessage("Pola nie mogą być puste");
                       } else {
                           query = "SELECT login FROM users WHERE login = ?";
                           Query.execute(query, username);
                           if(!Query.resultSet.next()) {
                               task.cancel();
                               updateMessage("Nie ma takiego użytkownika");
                           } else {
                               query = "SELECT password FROM users WHERE login = ?";
                               Query.execute(query, username);
                               if(Query.resultSet.next()) {
                                   String passwordValue = Query.resultSet.getString(1);
                                   if(!password.equals(passwordValue)) {
                                       task.cancel();
                                       updateMessage("Podane hasło jest błędne");
                                   } else {
                                       task.cancel();
                                       updateMessage("");
                                       isValid = true;
                                   }
                               }
                           }
                       }
                   } catch(InterruptedException e) {
                       if(isCancelled()) {
                           break;
                       }
                   }
               }
               return null;
            }
        };
        Status.get().bind(task.messageProperty());
        new Thread(task).start();
    }

    public static boolean isValid() {
        return isValid;
    }
}

从另一个类调用

  private void login() {
        if( SqlConnection.isConnected()) {
            try{
                Authorization.verifyLogin(String.valueOf(loginInput.getText()), String.valueOf(passwordInput.getText()));
                if(Authorization.isValid()) {
                    //should go to next menu
                    //but never does
                }
            } catch (SQLException e) {
                e.printStackTrace();
                Debug.log(e.toString());
            }
        }
    }

编辑# 对不起updateMessage() 的润色。

【问题讨论】:

  • 代码很混乱。任务中while 循环的目的是什么?看起来您在循环的第一次迭代中调用了cancel(),在所有可能的条件下,那么为什么要使用循环呢?无论如何,您的 verifyLogin() 方法只是在另一个线程中启动验证过程,然后立即退出。 isValid 标志在该线程完成之前不会更改,这会在很久以后发生。如果你想做验证过程,然后做其他事情,那么管理verifyLogin()中的线程真的没有意义。

标签: multithreading javafx concurrency


【解决方案1】:

您的verifyLogin() 方法只是在另一个线程中启动验证过程,然后立即退出。 isValid 标志在该线程完成之前不会更改,这会在很久以后发生。如果你想做验证过程,然后再做其他事情,管理verifyLogin()中的线程真的没有意义。

我不太了解您的代码应该做什么;你有一个while(...) 循环,据我所知,它只能执行一次(所以是多余的)。您似乎还执行了两个基本相同的 SQL 查询。 (第一个检查是否存在具有特定条件的行,如果有,则第二个检索该行。为什么不直接检索该行并检查它是否存在?)

我将对此进行重构,以便 validateLogin() 方法根本不处理线程,而只返回验证结果(例如状态字符串,但可能还有其他合适的)。

/**
  * @return An empty string if the login is valid, or an error message otherwise
  */

public static String verifyLogin(String username, String password) throws SQLException{
   isValid = false ;
   if(username.equals("") || password.equals("")) {
       return "Pola nie mogą być puste";
   } 
   query = "SELECT login, password FROM users WHERE login = ?";
   Query.execute(query, username);
   if(!Query.resultSet.next()) {
       return "Nie ma takiego użytkownika";
   } 

   String passwordValue = Query.resultSet.getString(2);
   if(!password.equals(passwordValue)) {
       return "Podane hasło jest błędne" ;
   } 


   isValid = true;
   return "" ;

}

现在我将通过login() 方法管理线程。这样您就可以在任务完成时使用任务的onSucceeded 处理程序来执行代码:

private void login() {
    if( SqlConnection.isConnected()) {
        Task<String> verifyTask = new Task<String>() {
            @Override
            protected String call() throws SQLException {

                return Authorization.verifyLogin(loginInput.getText(), passwordInput.getText());
            }
        };

        // probably better to use a progress indicator or similar here, but:

        Animation animation = new Timeline(
            new KeyFrame(Duration.ZERO, e -> Status.get().set("Weryfikacja.")),
            new KeyFrame(Duration.millis(200), e -> Status.get().set("Weryfikacja..")),
            new KeyFrame(Duration.millis(400), e -> Status.get().set("Weryfikacja...")),
            new KeyFrame(Duration.millis(600)));
        animation.setCycleCount(Animation.INDEFINITE);


        verifyTask.setOnSucceeded(event -> {
            animation.stop();
            Status.get().set(verifyTask.getValue());
            if(Authorization.isValid()) { // or if (verifyTask.getValue().isEmpty()) 
                // go to next menu
            }
        });

        verifyTask.setOnFailed(event -> {
            animation.stop();
            verifyTask.getException().printStackTrace();
            Debug.log(verifyTask.getException().toString());
        }

        animation.play();
        new Thread(verifyTask()).start();
    }
}

【讨论】:

  • 这确实有效,但实际上我之前设法达到了这一点,而无需使用线程。我开始使用它的原因是因为我想获得“等待效果”。有几种情况,例如查询数据库或打开连接,程序会短暂冻结。我想我只是在状态之后添加“动画”点,所以一旦用户点击冻结程序的东西,他会看到当前状态,如“正在连接”。 -> “正在连接..” -> “正在连接...”等,而不是程序冻结。不知道如何在程序等待一些数据时让那些“点浮动”
  • @user9309329 如果您不使用线程,则在进程运行时 UI 将完全无响应(没有其他控件将起作用,UI 无法重绘等)。这不是真的。你可以,例如例如,在启动线程之前立即启动动画或显示进度指示器并在onSucceeded 中停止/删除它。
  • 好吧,我以前没有线程的方法根本没有进度指示器,一旦我决定添加它们,我意识到我需要线程来完成这项工作,但后来我在这个领域有点迷失了,因为我以前从未使用过它们。所以基本上,我需要在verifyTask 之前开始另一个任务,按照我的意愿更新我的statusLabel,并在“verifyTask”完成后停止?
  • @user9309329 在其中添加了动画代码。无需使用线程来创建动画。同样,您可以简单地使用进度指示器,而不是自己编写。
猜你喜欢
  • 1970-01-01
  • 2012-07-23
  • 1970-01-01
  • 2015-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-17
相关资源
最近更新 更多