【问题标题】:How can I catch Event Dispatch Thread (EDT) exceptions?如何捕获事件调度线程 (EDT) 异常?
【发布时间】:2011-05-25 19:31:15
【问题描述】:

我正在使用一个名为 MyExceptionHandler 的类来实现 Thread.UncaughtExceptionHandler 来处理我的项目中的正常异常。

据我了解,此类无法捕获 EDT 异常,因此我尝试在 main() 方法中使用它来处理 EDT 异常:

public static void main( final String[] args ) {
    Thread.setDefaultUncaughtExceptionHandler( new MyExceptionHandler() );  // Handle normal exceptions
    System.setProperty( "sun.awt.exception.handler",MyExceptionHandler.class.getName());  // Handle EDT exceptions
    SwingUtilities.invokeLater(new Runnable() {  // Execute some code in the EDT. 
        public void run() {
            JFrame myFrame = new JFrame();
             myFrame.setVisible( true );
        }
    });
}

但直到现在它还没有工作。例如,在初始化 JFrame 时,我从构造函数中的捆绑文件加载其标签,如下所示:

setTitle( bundle.getString( "MyJFrame.title" ) );

我从捆绑文件中删除了密钥MyJFrame.title 以测试异常处理程序,但它不起作用!异常通常打印在日志中。

我在这里做错了吗?

【问题讨论】:

标签: java exception try-catch event-dispatch-thread


【解决方案1】:

EDT 异常处理程序不使用Thread.UncaughtExceptionHandler。相反,它调用具有以下签名的方法:

public void handle(Throwable thrown);

将其添加到MyExceptionHandler,它应该可以工作。

这方面的“文档”可以在EventDispatchThread 中找到,它是java.awt 中的一个包私有类。从handleException()那里引用javadoc:

/**
 * Handles an exception thrown in the event-dispatch thread.
 *
 * <p> If the system property "sun.awt.exception.handler" is defined, then
 * when this method is invoked it will attempt to do the following:
 *
 * <ol>
 * <li> Load the class named by the value of that property, using the
 *      current thread's context class loader,
 * <li> Instantiate that class using its zero-argument constructor,
 * <li> Find the resulting handler object's <tt>public void handle</tt>
 *      method, which should take a single argument of type
 *      <tt>Throwable</tt>, and
 * <li> Invoke the handler's <tt>handle</tt> method, passing it the
 *      <tt>thrown</tt> argument that was passed to this method.
 * </ol>
 *
 * If any of the first three steps fail then this method will return
 * <tt>false</tt> and all following invocations of this method will return
 * <tt>false</tt> immediately.  An exception thrown by the handler object's
 * <tt>handle</tt> will be caught, and will cause this method to return
 * <tt>false</tt>.  If the handler's <tt>handle</tt> method is successfully
 * invoked, then this method will return <tt>true</tt>.  This method will
 * never throw any sort of exception.
 *
 * <p> <i>Note:</i> This method is a temporary hack to work around the
 * absence of a real API that provides the ability to replace the
 * event-dispatch thread.  The magic "sun.awt.exception.handler" property
 * <i>will be removed</i> in a future release.
 */

Sun 究竟是如何期望你找到这个的,我不知道。

这是一个在 EDT 内外捕获异常的完整示例:

import javax.swing.SwingUtilities;

public class Test {
  public static class ExceptionHandler
                                   implements Thread.UncaughtExceptionHandler {

    public void handle(Throwable thrown) {
      // for EDT exceptions
      handleException(Thread.currentThread().getName(), thrown);
    }

    public void uncaughtException(Thread thread, Throwable thrown) {
      // for other uncaught exceptions
      handleException(thread.getName(), thrown);
    }

    protected void handleException(String tname, Throwable thrown) {
      System.err.println("Exception on " + tname);
      thrown.printStackTrace();
    }
  }

  public static void main(String[] args) {
    Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
    System.setProperty("sun.awt.exception.handler",
                       ExceptionHandler.class.getName());

    // cause an exception on the EDT
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        ((Object) null).toString();        
      }
    });

    // cause an exception off the EDT
    ((Object) null).toString();
  }
}

应该可以的。

【讨论】:

  • @uckelman ...我做了你提到的,但没有发现异常! ... public void handle( Throwable throw ) { System.out.println("Exception is Catched" ); // 机器人打印了! }
  • @Brad: "而且即使handle(...)不存在,那么异常会被实现的方法uncaughtException(...)处理,那么就不需要handle (……)!”这是错误的。 EDT 上未捕获的异常永远不会转到 Thread.UncaughtException 处理程序,因为 EDT 不是常规线程。如果您的异常没有通过示例中的异常处理程序采取任何路径,那么您就没有未捕获的异常——它在某处被捕获。
  • @uckelman ...在调查了我的代码之后,你是对的 uckelman。我的代码中的一部分捕获了 EDT 异常。但是对于 handle(...) 方法,它不是必需的。只需设置系统属性就足够了: System.setProperty("sun.awt.exception.handler",MyExceptionHandler.class.getName()); ... 我敢肯定。我创建了一个完整的示例来测试它。你也可以试试这个。非常感谢您的帮助。
  • @Brad:我认为java.awt.EventDispatchThread 自从我上次查看以来发生了一些变化——也就是说,如果handleException() 返回 false,processException() 会重新抛出异常,如果你有在您使用 sun.awt.exception.handler 属性指定的处理程序中没有 handle() 方法。因此,至少在当前版本的 Java 6 中,我猜你可以只使用Thread.UncaughtExceptionHandler。很高兴知道这一点,但它可能无法在旧版本的 Java 中正常工作。
  • 我遇到了这个非常有趣的主题,并且解决方案运行良好。我可以确认在 Java 7 中不需要 handle() 方法,我们也无法在 Javadoc 中找到它的任何踪迹。我现在能够正确记录未捕获的异常,感谢你们! :) 但是,异常跟踪仍在 stderr 上打印。有没有办法避免这种情况?
【解决方案2】:

仅提供一些额外信息,在许多情况下,即使在 1.5 和 1.6 中,Throwables 也可能被 EDT 的 UncaughtExceptionHandler 捕获。查看1.5.0_22中EventDispatchThread的源码:

private void processException(Throwable e, boolean isModal) {
    if (!handleException(e)) {
        // See bug ID 4499199.
        // If we are in a modal dialog, we cannot throw
        // an exception for the ThreadGroup to handle (as added
        // in RFE 4063022).  If we did, the message pump of
        // the modal dialog would be interrupted.
        // We instead choose to handle the exception ourselves.
        // It may be useful to add either a runtime flag or API
        // later if someone would like to instead dispose the
        // dialog and allow the thread group to handle it.
        if (isModal) {
            System.err.println(
                "Exception occurred during event dispatching:");
            e.printStackTrace();
        } else if (e instanceof RuntimeException) {
            throw (RuntimeException)e;
        } else if (e instanceof Error) {
            throw (Error)e;
        }
    }
}

private boolean handleException(Throwable thrown) {

    try {

        if (handlerClassName == NO_HANDLER) {
            return false;   /* Already tried, and failed */
        }

        /* Look up the class name */
        if (handlerClassName == null) {
            handlerClassName = ((String) AccessController.doPrivileged(
                new GetPropertyAction(handlerPropName)));
            if (handlerClassName == null) {
                handlerClassName = NO_HANDLER; /* Do not try this again */
                return false;
            }
        }

        /* Load the class, instantiate it, and find its handle method */
        Method m;
        Object h;
        try {
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            Class c = Class.forName(handlerClassName, true, cl);
            m = c.getMethod("handle", new Class[] { Throwable.class });
            h = c.newInstance();
        } catch (Throwable x) {
            handlerClassName = NO_HANDLER; /* Do not try this again */
            return false;
        }

        /* Finally, invoke the handler */
        m.invoke(h, new Object[] { thrown });

    } catch (Throwable x) {
        return false;
    }

    return true;
}

根据这段代码,只有 3 种方式 Throwable 不会被 EDT 线程的 UncaughtExceptionHandler 捕获:

  1. Throwable 已由 sun.awt.exception.handler 成功处理(已找到、实例化该类并调用其 handle(Throwable) 方法而不抛出任何东西)
  2. EDT 处于模式对话框中
  3. Throwable 既不是 RuntimeException 也不是错误

【讨论】:

    【解决方案3】:

    总结以上内容...使用较新的 Java,您可以这样做:

    // Log exceptions thrown on the event dispatcher thread
    SwingUtilities.invokeLater(()
      -> Thread.currentThread().setUncaughtExceptionHandler((thread, t)
      -> this.log.error("exception in event dispatcher thread", t)));
    

    【讨论】:

      猜你喜欢
      • 2011-01-27
      • 1970-01-01
      • 2013-01-02
      • 1970-01-01
      • 1970-01-01
      • 2011-09-26
      • 2015-03-14
      • 2010-12-27
      相关资源
      最近更新 更多