2

我正在开发一个在 jframe 中有登录表单的 java 应用程序。我有文本字段和按钮。

登录按钮有一个事件监听器,它是创建登录窗口的类的内部类。当用户按下登录按钮时,侦听器从字段中获取值并将其传递给验证器,该验证器使用 mysql 数据库对其进行验证,并根据用户的输入返回 true 和 false。现在基于返回值,监听器使用if-else语句更新 ui。整个工作都很好。

问题是在执行验证时无法使用 gui,因为每件事都是用一个线程完成的。所以在那段时间里gui有点冻结。如何使用多线程来避免此问题并在执行验证时使用其他 gui 组件。

4

5 回答 5

3

正如您可能知道的那样,您永远不应该在事件调度线程中执行长时间运行的任务,这会使您的程序看起来像是挂起的。

同样,您不应该在事件调度线程之外创建/修改任何 UI 组件。

最简单的解决方案之一是使用 SwingWorker。这允许您在后台线程中执行代码,但它会自动将其结果重新同步回事件调度线程......

public class LoginForm {

    public static void main(String[] args) {
        new LoginForm();
    }

    public LoginForm() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                JDialog frame = new JDialog((JFrame) null, "Login", true);
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new LoginPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
                System.exit(0);
            }
        });
    }

    public class LoginPane extends JPanel {

        private JTextField userNameField;
        private JPasswordField passwordField;
        private JButton okay;
        private JButton cancel;

        public LoginPane() {

            setLayout(new BorderLayout());

            userNameField = new JTextField(15);
            passwordField = new JPasswordField(10);

            okay = new JButton("Login");
            cancel = new JButton("Cancel");

            JPanel mainPane = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.EAST;
            gbc.insets = new Insets(2, 2, 2, 2);
            mainPane.add(new JLabel("User Name:"), gbc);
            gbc.gridy++;
            mainPane.add(new JLabel("Password:"), gbc);

            gbc.gridx++;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.WEST;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            mainPane.add(userNameField, gbc);
            gbc.gridy++;
            mainPane.add(passwordField, gbc);
            mainPane.setBorder(new EmptyBorder(8, 8, 8, 8));

            add(mainPane);

            JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
            buttonPane.setBorder(new EmptyBorder(8, 8, 8, 8));
            buttonPane.add(okay);
            buttonPane.add(cancel);

            add(buttonPane, BorderLayout.SOUTH);

            okay.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    userNameField.setEnabled(false);
                    passwordField.setEnabled(false);
                    okay.setEnabled(false);
                    cancel.setEnabled(false);
                    new LoginWorker(userNameField.getText(), passwordField.getPassword()).execute();
                }
            });

            cancel.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    SwingUtilities.getWindowAncestor(LoginPane.this).dispose();
                }
            });
        }

        public class LoginWorker extends SwingWorker<Boolean, Boolean> {

            private String userName;
            private char[] password;

            public LoginWorker(String userName, char[] password) {
                this.userName = userName;
                this.password = password;
            }

            @Override
            protected Boolean doInBackground() throws Exception {
                // Do you background work here, query the database, compare the values...
                Thread.sleep(2000);
                return Math.round((Math.random() * 1)) == 0 ? new Boolean(true) : new Boolean(false);
            }

            @Override
            protected void done() {
                System.out.println("Done...");
                try {
                    if (get()) {
                        JOptionPane.showMessageDialog(LoginPane.this, "Login sucessful");
                    } else {
                        JOptionPane.showMessageDialog(LoginPane.this, "Login failed");
                    }
                    userNameField.setEnabled(true);
                    passwordField.setEnabled(true);
                    okay.setEnabled(true);
                    cancel.setEnabled(true);
                } catch (Exception exp) {
                    exp.printStackTrace();
                }
            }

        }

    }
}

查看Concurrency in Swing了解更多信息,特别是SwingWorker

于 2013-01-08T08:28:04.233 回答
1

您可以使用线程。棘手的一点是确保您需要对 UI 进行的任何更新都在 UI 线程上完成。有一个SwingUtilities助手类。

例如。

new Thread(){
    public void run() {
        // do the background work...

        SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                 // update the UI
             }
        });
    }
}.start();

如果您想管理线程的创建方式,您还可以考虑使用后台执行程序。

ExecutorService backgroundExector = Executors.newFixedThreadPool(1);

(因此,您可以为您的应用程序在多个地方使用其中一个)。

于 2013-01-08T07:39:48.460 回答
0

按下“登录”(从按钮侦听器),禁用按钮并启动登录线程。该线程必须连接数据库并在self或login组件类上设置一些字段,告知登录成功。try {} finally {}用和在最后部分包围登录代码,调用SwingUtilities.invokeLater。现在,Runnable这个方法作为参数,您可以重新启用按钮,显示“ok”或“login failed”并执行其他操作。

于 2013-01-08T07:39:28.707 回答
0

那么开始一个新线程相对容易。

Thread thread = new Thread(Runnable r);
thread.start();

虽然 r 需要实现 Runnable 并包含用于检查用户名/密码的代码,但run().

new Runnable() {
@Override
public void run() {
        // your sql-code goes here.
    }
};

实际问题是同步您需要从侦听器和 sql 代码访问的任何变量。

同步变量很容易。

class example {
  private synchronized Integer mySyncInteger = new Integer();
}

执行你的 sql 代码后,我会简单地从视图(你做你的挥杆工作的地方)触发一些函数,比如swingClass.pushResult(Boolean). 这个函数根据给定的结果改变视图。从该函数更新视图时要小心,因为这仍然在您创建的线程中工作。

于 2013-01-08T07:41:09.320 回答
0

您可以创建一个线程来与数据库进行通信以验证用户和密码。但是,UI 更新只能从 Event Dispatcher 线程中完成。您可以使用SwingUtilities.invokeLater()orSwingUtilities.invokeAndWait()方法来做到这一点。

所以你的线程可以这样实现:

public void run()
{
    // logic to validate user/password

    if(valid == true)
    {
        SwingUtilities.invokeLater(/*Runnable to update the UI*/));
    }
}
于 2013-01-08T07:48:04.683 回答