使用多线程实现java gui登录

时间:2013-01-08 07:18:27

标签: java multithreading swing user-interface

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

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

问题在于,当执行验证时,不能使用gui,因为每个事情都是用单个线程完成的。所以那时gui有点冻结了。 如何在执行验证时使用多线程来避免此问题并使用其他gui组件。

5 个答案:

答案 0 :(得分:3)

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

同样,您不应该在Event Dispatching Thread之外创建/修改任何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

答案 1 :(得分:1)

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

例如

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

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

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

ExecutorService backgroundExector = Executors.newFixedThreadPool(1);

(所以你的应用程序中有一个用于多个地方)。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

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

虽然r需要实现Runnable并包含用于在run()内使用sql库检查用户名/密码的代码。

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

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

同步变量很容易。

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

在执行你的sql代码之后,我只会从视图(你在哪里进行你的工作)中触发一些函数,比如swingClass.pushResult(Boolean)。此函数根据给定的结果更改视图。小心从该函数更新视图,因为这仍然在您创建的线程中工作。

答案 4 :(得分:0)

您可以创建一个与数据库进行通信的线程,以验证用户和密码。但是,UI更新应该只从Event Dispatcher线程完成。您可以使用SwingUtilities.invokeLater()SwingUtilities.invokeAndWait()方法执行此操作。

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

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

    if(valid == true)
    {
        SwingUtilities.invokeLater(/*Runnable to update the UI*/));
    }
}