5

我们都知道我们应该从事件调度线程执行所有与 GUI 相关的任务,否则可能会引入奇怪的错误 - 我试着记住这条规则,但我必须承认我最近注意到了几个我没有注意到的地方。

有没有办法识别所有违反此规则的行为,以便修复它们?我已经看到这里有一个相关的 findbugs 规则但它似乎并没有为我捕获所有案例。即使在发生违规时抛出异常也会很好,因此我可以修复它(或捕获异常并记录警告,以防用户遇到相关问题。)

人们通常对此采取什么方法?

4

4 回答 4

6

一种方法是安装自定义重绘管理器,该管理器检测并记录在除 EDT 之外的线程上执行绘制时的情况。我们在我们的项目中使用这种方法,改编自以下博客:http ://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html 。这不会检测到所有类型的 EDT 线程违规,但它肯定比没有好得多。

更新:

正如评论者指出的那样,链接的网页不再可用。这是我从博客文章中改编的代码:

import javax.swing.JComponent;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
import org.apache.log4j.Logger;
import sun.awt.AppContext;

public class DetectEdtViolationRepaintManager extends RepaintManager {

  private static final Logger LOGGER = Logger.getLogger(
    DetectEdtViolationRepaintManager.class);

  /**
   * Used to ensure we only print a stack trace once per abusing thread.  May
   * be null if the option is disabled.
   */
  private ThreadLocal alreadyWarnedLocal;

  /**
   * Installs a new instance of DetectEdtViolationRepaintManager which does not
   * warn repeatedly, as the current repaint manager.
   */
  public static void install() {
    install(false);
  }

  /**
   * Installs a new instance of DetectEdtViolationRepaintManager as the current
   * repaint manager.
   * @param warnRepeatedly whether multiple warnings should be logged for each
   *        violating thread
   */
  public static void install(boolean warnRepeatedly) {
    AppContext.getAppContext().put(RepaintManager.class, 
      new DetectEdtViolationRepaintManager(warnRepeatedly));
    LOGGER.info("Installed new DetectEdtViolationRepaintManager");
  }

  /**
   * Creates a new instance of DetectEdtViolationRepaintManager.
   * @param warnRepeatedly whether multiple warnings should be logged for each
   *        violating thread
   */
  private DetectEdtViolationRepaintManager(boolean warnRepeatedly) {
    if (!warnRepeatedly) {
      this.alreadyWarnedLocal = new ThreadLocal();
    }
  }

  /**
   * {@inheritDoc}
   */
  public synchronized void addInvalidComponent(JComponent component) {
    checkThreadViolations();
    super.addInvalidComponent(component);
  }

  /**
   * {@inheritDoc}
   */
  public synchronized void addDirtyRegion(JComponent component, int x, int y, 
    int w, int h) {
    checkThreadViolations();
    super.addDirtyRegion(component, x, y, w, h);
  }

  /**
   * Checks if the calling thread is called in the event dispatch thread.
   * If not an exception will be printed to the console.
   */
  private void checkThreadViolations() {
    if (alreadyWarnedLocal != null && Boolean.TRUE.equals(alreadyWarnedLocal.get())) {
      return;
    }
    if (!SwingUtilities.isEventDispatchThread()) {
      if (alreadyWarnedLocal != null) {
        alreadyWarnedLocal.set(Boolean.TRUE);
      }
      LOGGER.warn("painting on non-EDT thread", new Exception());
    }
  }
}
于 2011-12-16T03:18:50.433 回答
4

我只是尽量小心,我自己。但是您可以使用 SwingUtilities.isEventDispatchThread() 安装代码来测试给定的代码段是否正在调度线程中执行,如果不是(或者如果是)则执行您喜欢的操作。

于 2011-12-16T01:55:12.997 回答
3

Substance Look and Feel包括一个自动运行时 EDT 违规检查器。它可以帮助在测试时发现 EDT 违规。当检测到违规时,它会抛出 IllegalStateException。有利于测试。

于 2011-12-16T01:58:48.280 回答
0

Fest 的 swing 模块还包括一个您可以安装的EDT 违规检查器,它在检测到违规时会引发异常。

于 2013-12-17T16:20:48.410 回答