首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >GTKLookAndFeel会导致Ubuntu 20上的死锁

GTKLookAndFeel会导致Ubuntu 20上的死锁
EN

Stack Overflow用户
提问于 2022-02-17 14:27:09
回答 1查看 54关注 0票数 1

在运行以下代码时,遇到了一个奇怪的问题:

代码语言:javascript
复制
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返回的),还是在这里做了一些错误/意外的事情?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-17 15:33:10

我在这里做错了什么事吗?

是的,你做错了什么:你违反了摆动线程策略,这是

除非另有文档说明,否则所有Swing组件和相关类都必须在事件调度线程上访问。

这个答案将向您展示如何重构您的代码,以便它遵守(和其他)线程规则。我没有一个Linux环境来测试这一点,所以在JavaFX和/或Swing的Linux实现中或者在GTKLookAndFeel中也可能有but,但是这至少给您提供了一种正确的测试方法。

main方法中的大多数代码应该在AWT事件分派线程上执行。您可以通过在SwingUtilities.invokeLater(...)中包装它来实现这一点。

这方面的例外是:

  1. Platform.runLater(...)计划在from线程上运行一些东西,它本身可以从任何地方调用。但是,如果在上执行new JPanel()而不是,那么它应该在AWT事件分派线程上执行。
  2. 不能从AWT事件分派线程调用SwingUtilities.invokeAndWait(...) 。这将安排在AWT事件分派线程上执行的代码,并暂停当前线程直到完成。因此,从AWT事件分派线程调用SwingUtilities.invokeAndWait(...)将导致死锁。密码 SwingUtilities.invokeAndWait(JFXPanel:new); 调度在AWT事件调度线程上执行的new JFXPanel(),并阻塞当前线程,直到完成为止。如果您已经在AWT事件分派线程上,只需调用 新JFXPanel(); 实现同样的目标。

我知道您的代码试图创建一个最小可复制的示例,但并不清楚它是什么示例,因为其中有一些代码的用途完全不明确。例如,创建两个JFXPanel实例,第一个实例被丢弃。并在不正确的线程上创建一个JPanel,类似地将其丢弃。

我没有一个Linux环境来测试这一点,但是您发布的代码中最接近的“正确”版本是这样的。如果这段代码仍然挂起,我相信在某个库实现中存在一个bug。

代码语言:javascript
复制
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工具包,除非您有一个非常有说服力的理由这样做。正如您所看到的,使用两个独立的单线程工具箱是很有挑战性的。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71159983

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档