jav*_*top 2 java events swing multithreading awt
我的完整 GUI 在 AWT 线程内运行,因为我使用SwingUtilities.invokeAndWait(...).
现在我有一个 JDialog,它只显示一个JLabel,表示某个作业正在进行中,并在作业完成后关闭该对话框。
问题是:标签没有显示。JDialog这项工作似乎在完全布置好之前就开始了。
当我只是打开对话框而不等待作业并关闭时,就会显示标签。
对话框在其构造函数中执行的最后一件事是setVisible(true)。
诸如revalidate()、repaint()、 ... 之类的东西也没有帮助。
即使我为受监视的作业启动一个线程,并使用它等待someThread.join()它也没有帮助,因为join我猜当前线程(即 AWT 线程)被 阻塞了。
替换JDialog为JFrame也没有帮助。
那么,这个概念总体上是错误的吗?或者在确保 a (或) 完全布局后我可以管理它来完成某些工作吗?JDialogJFrame
我想要实现的简化算法:
JDialog我设法编写了一个可重现的测试用例:
编辑答案中的问题现已解决:此用例确实显示标签,但由于对话框的模态,它在“模拟过程”后无法关闭。
import java.awt.*;
import javax.swing.*;
public class _DialogTest2 {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
final JLabel jLabel = new JLabel("Please wait...");
@Override
public void run() {
JFrame myFrame = new JFrame("Main frame");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
JDialog d = new JDialog(myFrame, "I'm waiting");
d.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
d.add(jLabel);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000); // simulate process
jLabel.setText("Done");
} catch (InterruptedException ex) {
}
}
});
d.setVisible(false);
d.dispose();
myFrame.setVisible(false);
myFrame.dispose();
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
尝试这个:
package javaapplication3;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Main {
public static void main(String[] args)
throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
final JLabel jLabel = new JLabel("Please wait...");
@Override
public void run() {
JFrame myFrame = new JFrame("Main frame");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
JDialog d = new JDialog(myFrame, "I'm waiting");
d.add(jLabel);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
new Thread(new Runnable() {
@Override
public void run() {
public void run() {
try {
Thread.sleep(3000); // simulate process
jLabel.setText("Done"); // HERE: should be done on EDT!
} catch (InterruptedException ex) {
}
}
}).start();
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
这个可行,但不正确。我会解释发生了什么事。
您的main()方法从“主”线程开始。所有与 Swing 相关的代码都应该在 EDT 线程上完成。这就是您使用(正确)的原因SwingUtilities.invokeAndWait(...)。到目前为止,一切都很好。
但 EDT 上不应该有长时间运行的任务。由于 Swing 是单线程的,任何长时间运行的进程都会阻塞 EDT。所以你的代码Thread.wait(...)永远不应该在 EDT 上执行。这是我的修改。我将调用包装在另一个线程中。所以这是 Swing 惯用的长时间运行任务处理。为了简洁起见,我使用 Thread 类,但我真的建议使用SwingWorker线程。
非常重要的是:我在前面的示例中犯了一个错误。看到带有“HERE”注释的行了吗?这是另一个 Swing 单线程规则违规。线程内的代码在 EDT之外运行,因此它永远不应该接触 Swing。所以这段代码不符合 Swing 单线程规则。冻结 GUI 并不安全。
如何纠正这个问题?简单的。您应该将调用包装在另一个线程中并将其放入 EDT 队列中。所以正确的代码应该是这样的:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jLabel.setText("Done");
}
});
Run Code Online (Sandbox Code Playgroud)
编辑:这个问题涉及很多与 Swing 相关的问题。无法一次解释所有这些......但这里还有一个片段,它可以满足您的需求:
public static void main(String[] args)
throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
final JFrame myFrame = new JFrame("Main frame");
final JLabel jLabel = new JLabel("Please wait...");
final JDialog d = new JDialog(myFrame, "I'm waiting");
@Override
public void run() {
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
d.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
d.add(jLabel);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000); // simulate process
System.out.println("After");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
d.setVisible(false);
d.dispose();
myFrame.setVisible(false);
myFrame.dispose();
}
});
} catch (InterruptedException ex) {
}
}
}).start();
d.setVisible(true);
}
});
}
Run Code Online (Sandbox Code Playgroud)
总结:
SwingUtilities....
invokeAndWait()- 顾名思义,调用是同步的,invokeLater()-“有时”调用代码,但立即返回Thread(将 Runnable 传递给新线程或覆盖它的start()方法)并启动它,SwingWorker创建一个有一些额外内容的新线程。典型的 GUI 场景包括:
1.、2.、3. 和 4. 在EDT上运行。4.不应该。有很多方法可以编写正确的线程代码。最麻烦的是使用Thread早期版本的 Java 附带的类。如果天真地这样做,可能会浪费资源(同时运行太多线程)。而且更新 GUI 也很麻烦。使用 SwingWorker 可以稍微缓解这个问题。它保证在启动、运行和更新 GUI 时行为正常(每个方法都有一个专用方法,您可以覆盖该方法并确保它在正确的线程上运行)。