在运行以下代码时,遇到了一个奇怪的问题:
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JFrame frame = new JFrame("Test");
frame.setLocationRelativeTo(null);
SwingUtilities.invokeAndWait(JFXPanel::new);
frame.add(new JFXPanel());
Platform.runLater(() -> {
JPanel panel = new JPanel();
System.exit(0);
});
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(200, 100);
frame.setVisible(true);
}首先,代码是用OpenJDK-11和OpenJFX-11编译的,在Windows中运行得很好(即在System.exit(0)调用时退出)。
但是,如果我在Linux上运行这个程序(特别是Ubuntu20.04),那么对new JPanel()的调用就会锁定线程,程序就永远不会退出。注释掉UIManager.setLookAndFeel调用将使它再次正常工作。
我只是在com.sun.java.swing.plaf.gtk.GTKLookAndFeel中遇到了一个bug (这是SystemLookAndFeel返回的),还是在这里做了一些错误/意外的事情?
发布于 2022-02-17 15:33:10
我在这里做错了什么事吗?
是的,你做错了什么:你违反了摆动线程策略,这是
除非另有文档说明,否则所有Swing组件和相关类都必须在事件调度线程上访问。
这个答案将向您展示如何重构您的代码,以便它遵守(和其他)线程规则。我没有一个Linux环境来测试这一点,所以在JavaFX和/或Swing的Linux实现中或者在GTKLookAndFeel中也可能有but,但是这至少给您提供了一种正确的测试方法。
main方法中的大多数代码应该在AWT事件分派线程上执行。您可以通过在SwingUtilities.invokeLater(...)中包装它来实现这一点。
这方面的例外是:
Platform.runLater(...)计划在from线程上运行一些东西,它本身可以从任何地方调用。但是,如果在上执行new JPanel()而不是,那么它应该在AWT事件分派线程上执行。SwingUtilities.invokeAndWait(...) 。这将安排在AWT事件分派线程上执行的代码,并暂停当前线程直到完成。因此,从AWT事件分派线程调用SwingUtilities.invokeAndWait(...)将导致死锁。密码
SwingUtilities.invokeAndWait(JFXPanel:new);
调度在AWT事件调度线程上执行的new JFXPanel(),并阻塞当前线程,直到完成为止。如果您已经在AWT事件分派线程上,只需调用
新JFXPanel();
实现同样的目标。我知道您的代码试图创建一个最小可复制的示例,但并不清楚它是什么示例,因为其中有一些代码的用途完全不明确。例如,创建两个JFXPanel实例,第一个实例被丢弃。并在不正确的线程上创建一个JPanel,类似地将其丢弃。
我没有一个Linux环境来测试这一点,但是您发布的代码中最接近的“正确”版本是这样的。如果这段代码仍然挂起,我相信在某个库实现中存在一个bug。
public static void main(String[] args) throws Exception {
// We are not on the AWT event dispatch thread, so if we
// want to create and access Swing components, we must wrap
// that code inside SwingUtilities.invokeLater(...):
SwingUtilities.invokeLater(() -> {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JFrame frame = new JFrame("Test");
frame.setLocationRelativeTo(null);
// We must not call SwingUtilities.invokeAndWait(...)
// from the event dispatch thread, so remove this:
// SwingUtilities.invokeAndWait(JFXPanel::new);
// To create new JFXPanel and block until the call is
// complete, we just create it in the usual way (since
// we're already on the event dispatch thread)
new JFXPanel();
frame.add(new JFXPanel());
Platform.runLater(() -> {
// This code block is executed on the FX Application Thread
// To create a swing component, we must wrap the
// code in SwingUtilities.invokeLater(...), so it's
// executed on the Event Dispatch Thread:
SwingUtilities.invokeLater(() -> {
JPanel panel = new JPanel();
System.exit(0);
});
});
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(200, 100);
frame.setVisible(true);
});
}顺便提一下,外观和感觉只会影响Swing组件;它不会影响JavaFX控件,其外观和行为由JavaFX管理(默认情况下由默认的CSS样式表管理)。因此,您在JFXPanel中放置的任何东西都不会受到外观和感觉的影响。
最后,您不应该混合多个UI工具包,除非您有一个非常有说服力的理由这样做。正如您所看到的,使用两个独立的单线程工具箱是很有挑战性的。
https://stackoverflow.com/questions/71159983
复制相似问题