我有一些Java Swing登录面板。当用户单击“登录”按钮时,将进行大量的数据库通信。我有一个想法将数据库通信放入另一个线程(数据库通信用于填充一些哈希映射和单例)。在将该通信放入单独的线程之后,在用户输入uname和密码时会发生数据包重度提升。当用户点击“登录”按钮时,代码将等待“繁重”线程加入,然后继续。
问题在于,当我的代码正在等待数据库线程加入时,似乎无法进行UI更新。我不认为我可以使用SwingWorker进行数据库通信,因为用户可以随时点击“登录”按钮,甚至在SW完成之前我无法加入SwingWorker(数据库通信必须在实际登录之前进行)
有没有办法在等待另一个线程加入时启用Swing ui更新?我错过了什么吗?
答案 0 :(得分:3)
只需放置两个标志,并在两个操作结束时(DB通信和用户单击),调用相同的方法并验证两个标志的状态。如果要阻止用户输入,可能会在模式对话框中显示进度条:
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UnsupportedLookAndFeelException;
public class TestThreadJoin {
private boolean dbWorkDone = false;
private boolean credentialsProvided = false;
private JTextField loginTF;
private JTextField passwordTF;
protected void initUI() {
final JFrame frame = new JFrame(TestThreadJoin.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridBagLayout());
JLabel login = new JLabel("Login: ");
JLabel password = new JLabel("Password: ");
loginTF = new JTextField(20);
passwordTF = new JPasswordField(20);
GridBagConstraints gbc = new GridBagConstraints();
panel.add(login, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
panel.add(loginTF, gbc);
gbc.gridwidth = 1;
panel.add(password, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
panel.add(passwordTF, gbc);
JButton loginButton = new JButton("Login");
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
credentialsProvided = true;
proceed();
}
});
frame.add(panel);
frame.add(loginButton, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
Thread.sleep(10000);
return null;
}
@Override
protected void done() {
super.done();
dbWorkDone = true;
proceed();
}
};
worker.execute();
}
protected void proceed() {
if (credentialsProvided && dbWorkDone) {
System.err.println("Continuing ");
}
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TestThreadJoin().initUI();
}
});
}
}
答案 1 :(得分:2)
可以使用SwingWorker完成。 将此ActionListener添加到JButton。同时为'label'和amp;提供JLabel字段。 'progressbar'的JProgressBar字段。
java.awt.event.ActionListener al = new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
SwingWorker swingWorker = new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
// SIMULATE DB WORK
for (int i = 0; i < 10; i++) {
synchronized (this) {
this.wait(300);
}
publish(10 * i);
}
publish(100);
return 0;
}
@Override
protected void done() {
label.setText("Work complete");
button.setEnable(true);
}
@Override
protected void process(List chunks) {
for (Object object : chunks) {
Integer progress = (Integer) object;
progressBar.setValue(progress);
}
}
};
button.setEnable(false);
swingWorker.execute();
}
}
由于数据库工作可能无法量化(可衡量),请执行以下更改:
答案 2 :(得分:2)
我真的认为你应该通过做类似的事情来试试SwingWorker:
这样你就不会冻结EDT ......
另一种选择是运行你自己的“迷你摇摆工”:
loginButton.setEnabled(false); // assuming this runs in the EDT
new Thread(new Runnable() {
public void run() {
myOtherBgThread.join(); // here we wait for the BG thread
SwingUtilities.invokeLater(new Runnable() {
// go back into the EDT
// update the UI here...
}
}
}.start();
希望它有所帮助,问候
答案 3 :(得分:1)
最简单的解决方案之一是在初始数据库通信完成之前禁用“登录”按钮。但这需要一些额外的进度条或消息,初始化发生时不会混淆用户。在这种情况下,您可以使用SwingWorker
在完成后启用“登录”按钮。
另一个解决方案是,当用户按下Login按钮时,应该再次将处理委托给等待初始DB通信线程加入的(另一个)后台线程。这样就不会阻止EDT(和GUI)。实际连接到DB也是一个冗长的操作,因此无论如何都应该在后台线程中完成,即使在如1所述禁用“登录”按钮时也是如此。
另一个解决方案是创建一个固定大小为1的线程池(例如java.util.concurrent.Executors.newSingleThreadExecutor()
)并在那里传递所有后台任务。对于单线程执行程序,任务将按顺序执行。第一个任务是初始数据库通信,另一个任务是登录任务。
答案 4 :(得分:0)
使用ExecutorService。调用executorService.submit(runnable)
,其中'runnable'执行数据库工作。 submit
方法将返回Future
。然后在登录后你可以拨打future.get()
等待完成(或者如果runnable已经完成则立即返回)。