【问题标题】:Why does calling this method on the EDT cause a compilation error?为什么在 EDT 上调用此方法会导致编译错误?
【发布时间】:2014-11-27 06:35:01
【问题描述】:

我正在尝试弹出一个自定义对话框。当我尝试在 EDT 上调用该方法时,我收到以下错误:

Exception in thread "AWT-EventQueue-0" java.lang.Error: Unresolved compilation problem: 

at danind.com.gmail_coem.ui.CredentialEditor.promptPossibleDialog(CredentialEditor.java:29)
at danind.com.gmail_coem.ui.HomeScreen$ConfigureDatabase.<init>(HomeScreen.java:281)
at danind.com.gmail_coem.ui.HomeScreen.configureDatabase(HomeScreen.java:230)
at danind.com.gmail_coem.ui.HomeScreen.lambda$1(HomeScreen.java:105)
at danind.com.gmail_coem.ui.HomeScreen$$Lambda$7/2092062410.actionPerformed(Unknown Source)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$400(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

在 Eclipse 中清理我的项目并进行一些隔离测试后,我发现在 EDT 上调用该方法是导致问题的原因。当我将该方法移动到后台线程时,它可以工作,但我不希望这样做,因为我想在 EDT 上创建我的对话框 GUI。

//Creates compilation error
private class ConfigureDatabase extends SwingWorker<Void, String[]>
{
    private CredentialEditor instance;
    public ConfigureDatabase()
    { //Runs on EDT
        this.instance = CredentialEditor.promptPossibleDialog(true);
    }

    @Override
    protected Void doInBackground()
    { //Runs in background thread
        try(Database database = CredentialEditor.getCredentials(instance))
        {
            //code
        }
    }
}

//Runs just fine, but dialog GUI is not on EDT
private class ConfigureDatabase extends SwingWorker<Void, String[]>
{
    @Override
    protected Void doInBackground()
    { //Runs in background thread
        try(Database database = CredentialEditor.getCredentials(CredentialEditor.promptPossibleDialog(true)))
        {
            //code
        }
    }
}

有问题的方法:

public static CredentialEditor promptPossibleDialog(boolean reset)
{
    if(reset || ConnectionPool.getInstance() == null)
    { //Checks to see if a dialog box needs to be created.
        if(SwingUtilities.isEventDispatchThread())
        { //Checks to make sure the thread is on the EDT.
            return new CredentialEditor();
        }
        else
        { //If it's not on the EDT throw an exception warning.
            throw new IllegalStateException("Must run on EDT!");
        }
    }
    return null; //If no dialog box needs to be created return nothing.
}

要更详细地了解问题,似乎只是调用该方法会导致问题。它没有设置实例变量或方法内的任何内容,它只是专门在 EDT 中调用该静态方法。事实上,堆栈跟踪指向它只是说明方法的行,如它所说的行 public static CredentialEditor promptPossibleDialog(boolean reset)

那么是什么导致了错误,如果我无法解决它,我如何在 EDT 上运行我的 GUI 代码,即使它的方法是在后台线程上调用的?

【问题讨论】:

  • @VinceEmigh 这是他帖子中的第一件事:Unresolved compilation problem
  • 我们可以看看你的CredentialEditor类的其余代码吗?
  • @VinceEmigh 这就是这个问题对我来说很有趣的原因:) 他的编译器并没有告诉他到底是哪个错误;它只是说有一个编译错误
  • @VinceEmigh 这是 Eclipse 给我的完整堆栈跟踪。没有红线。它只是简单地调用 EDT 上的方法会导致该错误,将其移至后台线程可以正常工作(但显然不能在 EDT 上运行)。
  • @VinceEmigh 也许你是对的。阅读更多内容后,我得出结论,这是 Eclipse 中最常见的问题 (reference)。 Danthony,如果你尝试从命令行编译和运行会发生什么?

标签: java multithreading user-interface event-dispatch-thread


【解决方案1】:

尝试直接在 EDT 上运行您的对话框。

public ConfigureDatabase()
{
    //some code
    this.instance = CredentialEditor.promptPossibleDialog(true); //This is line 281
}

这意味着您正在工作线程中运行对话框,这不是一个好主意。工作线程主要用于非交互式后台任务。如果您必须从工作线程中运行对话框,则必须在 EDT 中单独启动它,例如:

public ConfigureDatabase()
{
    SwingUtilities.invokeLater(new Runnable() { //or if you must wait for its end, use invokeAndWait

        public void run() {
                CredentialEditor.promptPossibleDialog(true); //This is line 281
        }
    });
}

这应该可以。如果能提供一个完整的 SSCE 会更有帮助,首先是你如何执行你的 Worker。

对话框也用于与用户交互并返回结果。因此,将对话框保存在实例中并不是最好的选择。而是存储其结果或重新考虑您的设计。

here,一个例子:

注意:在 Event Dispatch Thread 上调用 get 会阻塞所有事件, 包括重绘,从被处理到这个 SwingWorker 被处理 完成。

当您希望 SwingWorker 阻塞事件调度线程时,我们 建议您使用模态对话框。

例如:

class SwingWorkerCompletionWaiter extends PropertyChangeListener {
     private JDialog dialog;

     public SwingWorkerCompletionWaiter(JDialog dialog) {
         this.dialog = dialog;
     }

     public void propertyChange(PropertyChangeEvent event) {
         if ("state".equals(event.getPropertyName())
                 && SwingWorker.StateValue.DONE == event.getNewValue()) {
             dialog.setVisible(false);
             dialog.dispose();
         }
     }
 }

运行方式:

     JDialog dialog = new JDialog(owner, true);
     swingWorker.addPropertyChangeListener(
         new SwingWorkerCompletionWaiter(dialog));
     swingWorker.execute();
     //the dialog will be visible until the SwingWorker is done
     dialog.setVisible(true);

指定者:get in interface Future 返回:计算结果 抛出:InterruptedException - 如果当前线程被中断 在等待 ExecutionException - 如果计算抛出 异常

【讨论】:

    【解决方案2】:

    您对 Eclipse 允许运行代码的特性感到困惑,即使代码存在编译错误,这弊大于利,恕我直言。您可以考虑将其关闭。但是,您必须了解的是,查看堆栈跟踪的行号是没有用的,因为它不一定会告诉您编译错误的行号,而是在运行时生成异常的行号.

    运行时不会尝试编译您的代码。相反,编译器生成的代码将在执行到达 Eclipse 无法编译的代码段时无条件地抛出该异常。所以它与你的代码运行的线程没有任何关系。由于虚假的编译器错误出现在CredentialEditor 中,所以令人难以置信的是,在调用者处所做的修改会改变它是否正确编译。但是,由于代码在后台线程中调用时会抛出 IllegalStateException,但您说它在您的第二种情况下有效,因此似乎有 项更改您没有告诉我们。

    另一方面,如果行为是由错误引起的,则不必看起来合乎逻辑。通常,异常包含编译器错误,但堆栈跟踪中的空行与 IDE 未告诉您有关该错误的观察到的行为完全匹配。因此,您遇到了一个错误,即在不存在编译器错误的情况下假定编译器错误,或者存在缺少消息的编译器错误。

    考虑到您没有提及您正在使用 Java 8 甚至积极使用新功能,遇到编译器错误并不奇怪。 Eclipse 对 Java 8 的支持……嗯……还有很大的改进空间。只需检查您是否真的在使用最新的 Eclipse 版本。如果您正在使用或更新后问题仍然存在,我建议您创建一个重现问题且不依赖任何其他(第 3 方)类的最小示例,并向 Eclipse 团队提交错误报告。

    【讨论】:

      猜你喜欢
      • 2010-09-18
      • 1970-01-01
      • 1970-01-01
      • 2015-06-18
      • 2018-11-19
      • 2011-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多