我正在研究一个在jframe中有登录表单的java应用程序。我有文本字段和按钮。
登录按钮有一个eventlistener,它是创建登录窗口的类的内部类。当用户按下登录按钮时,监听器从字段中获取值并将其传递给验证器,验证器使用mysql数据库对其进行验证,并根据用户的输入返回true和false。现在,基于返回值,侦听器使用if-else
语句更新ui。整件事情都很好。
问题在于,当执行验证时,不能使用gui,因为每个事情都是用单个线程完成的。所以那时gui
有点冻结了。
如何在执行验证时使用多线程来避免此问题并使用其他gui组件。
答案 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*/));
}
}