查找事件分派线程违规

5
我们都知道应该从事件分发线程执行所有与 GUI 相关的任务,否则可能会引入奇怪的错误。我尽力记住这个规则,但最近我注意到了一些违例。

有没有一种方法可以确定所有违反此规则的情况,以便进行修复?我看到有一个相关的 FindBugs 规则(在此处),但它似乎不能捕获所有情况。即使只是在违规发生时抛出异常,也很好,这样我就可以修复它(或者在用户遇到相关问题时捕获异常并记录警告)。

人们通常采用什么方法处理这个问题?


2
请参阅相关的 问答 - trashgod
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());
    }
  }
}

谢谢,这看起来很有前途 - 我会去看看! - Michael Berry
链接不再有效。你有新地址吗? - peterboston

4

我只是尽量小心谨慎。但是你可以安装代码来测试一个给定的代码是否在调度线程中执行,使用SwingUtilities.isEventDispatchThread(),如果它不是(或者如果它是),你可以根据自己的喜好进行操作。


3

你知道如何从GitHub上获取EDT违规检查器(仅限)吗? - peterboston

0

你有EDT违规检查器的新地址吗?旧地址已经失效了。 - peterboston

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接