5

我找到了許多關於正確使用EDT的教程和示例,但是我想聽聽如何正確使用EDT:檢查複雜的應用程序,它具有Swing GUI和許多涉及長網絡操作的功能,並找到EDT被不當使用的地方。如何檢查正確使用EDT的Swing應用程序(事件DIspatch線程)

我發現

SwingUtilities.isEventDispatchThread() 

可以用來檢查一個代碼片段是否是EDT內部或沒有,所以我可以檢查所有的長時間操作不正好是裏面的地方的SwingUtilities .isEventDispatchThread()返回true。

是不是?有沒有更好的方法可以調試整個應用程序來搜索EDT的不正確使用? 謝謝。

+3

看看:http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html – kiheru

+2

典型的檢查策略是倒過來:他們找個地方在EDT上訪問Swing組件(與檢查EDT上沒有發生長時間運行的代碼) - 從邏輯上講,如果沒有在懷疑長時間運行的地方添加代碼,則後者是不可能的 – kleopatra

+0

和如何找到您訪問EDT的Swing組件的地方? – dendini

回答

6

是不是?

是的,檢查SwingUtilities.isEventDispatchThread()的值是查看您的代碼是否在事件調度線程(EDT)上的一種方法。

另一種方式是顯示或打印Thread.currentThread().getName()。 EDT幾乎總是有名稱「AWT-EventQueue-0」。

這段漂亮的代碼來自文章Debugging Swing, the final summary。但是,它不是一個完整的Swing調試器。該代碼僅檢查重繪違規。

本文列出了更完整的其他調試方法。

import javax.swing.JComponent; 
import javax.swing.RepaintManager; 
import javax.swing.SwingUtilities; 

public class CheckThreadViolationRepaintManager extends RepaintManager { 
    // it is recommended to pass the complete check 
    private boolean completeCheck = true; 

    public boolean isCompleteCheck() { 
     return completeCheck; 
    } 

    public void setCompleteCheck(boolean completeCheck) { 
     this.completeCheck = completeCheck; 
    } 

    public synchronized void addInvalidComponent(JComponent component) { 
     checkThreadViolations(component); 
     super.addInvalidComponent(component); 
    } 

    public void addDirtyRegion(JComponent component, int x, int y, int w, int h) { 
     checkThreadViolations(component); 
     super.addDirtyRegion(component, x, y, w, h); 
    } 

    private void checkThreadViolations(JComponent c) { 
     if (!SwingUtilities.isEventDispatchThread() 
       && (completeCheck || c.isShowing())) { 
      Exception exception = new Exception(); 
      boolean repaint = false; 
      boolean fromSwing = false; 
      StackTraceElement[] stackTrace = exception.getStackTrace(); 
      for (StackTraceElement st : stackTrace) { 
       if (repaint && st.getClassName().startsWith("javax.swing.")) { 
        fromSwing = true; 
       } 
       if ("repaint".equals(st.getMethodName())) { 
        repaint = true; 
       } 
      } 
      if (repaint && !fromSwing) { 
       // no problems here, since repaint() is thread safe 
       return; 
      } 
      exception.printStackTrace(); 
     } 
    } 
} 
+0

我更感興趣的是驗證EDT的使用方法,而不是找到我在EDT內部還是外部的方法(也是因爲isEventDispatchThread似乎可以完成它的工作)。有人建議做相反的事情,「找到你訪問擺脫EDT的Swing組件的地方」 – dendini

2

檢查整個應用程序正確使用EDT的一種方法是使用java代理。下面的代碼是根據Debugging Swing, the final Summary發佈的代理的改進版本。它適用於ASM 4.1。創建一個包含asm-all-4.1.jar(解包)的Jar,編譯後的代碼以及一個將代理指定爲Premain-Class的清單並開始工作。

/** 
* A java agent which transforms the Swing Component classes in such a way that a stack 
* trace will be dumped or an exception will be thrown when they are accessed from a wrong thread. 
* 
* To use it, add 
* <pre> 
* -javaagent:${workspace_loc:MyProject/tool/util/swingEDTCheck}/swingEDTCheck.jar 
* </pre> 
* 
* to the VM arguments of a run configuration. This will cause the stack traces to be dumped. 
* 
* Use 
* <pre> 
* -javaagent:${workspace_loc:MyProject/tool/util/swingEDTCheck}/swingEDTCheck.jar=throw 
* </pre> 
* to throw exceptions. 
* 
*/ 
public class SwingEDTCheckAgent { 

    public static void premain(String args, Instrumentation inst) { 
     boolean throwing = false; 
     if ("throw".equals(args)) { 
      throwing = true; 
     } 
     inst.addTransformer(new Transformer(throwing)); 
    } 

    private static class Transformer implements ClassFileTransformer { 

     private final boolean throwing; 

     public Transformer(boolean throwing) { 
      this.throwing = throwing; 
     } 

     @Override 
     public byte[] transform(ClassLoader loader, 
      String className, 
      Class classBeingRedefined, 
      ProtectionDomain protectionDomain, 
      byte[] classfileBuffer) 
      throws IllegalClassFormatException { 
      // Process all classes in javax.swing package which names start with J 
      if (className.startsWith("javax/swing/J")) { 
       ClassReader cr = new ClassReader(classfileBuffer); 
       ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); 
       ClassVisitor cv = new EdtCheckerClassAdapter(cw, throwing); 
       cr.accept(cv, 0); 
       return cw.toByteArray(); 
      } 
      return classfileBuffer; 
     } 
    } 

    private static class EdtCheckerClassAdapter extends ClassVisitor { 

     private final boolean throwing; 

     public EdtCheckerClassAdapter(ClassVisitor classVisitor, boolean throwing) { 
      super(Opcodes.ASM4, classVisitor); 
      this.throwing = throwing; 
     } 

     @Override 
     public MethodVisitor visitMethod(final int access, 
      final String name, 
      final String desc, 
      final String signature, 
      final String[] exceptions) { 
      MethodVisitor mv = 
       cv.visitMethod(access, name, desc, signature, exceptions); 

      if (name.startsWith("set") || name.startsWith("get") || name.startsWith("is")) { 
       return new EdtCheckerMethodAdapter(mv, throwing); 
      } else { 
       return mv; 
      } 
     } 
    } 

    private static class EdtCheckerMethodAdapter extends MethodVisitor { 

     private final boolean throwing; 

     public EdtCheckerMethodAdapter(MethodVisitor methodVisitor, boolean throwing) { 
      super(Opcodes.ASM4, methodVisitor); 
      this.throwing = throwing; 
     } 

     @Override 
     public void visitCode() { 
      mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/awt/EventQueue", "isDispatchThread", "()Z"); 
      Label l1 = new Label(); 
      mv.visitJumpInsn(Opcodes.IFNE, l1); 
      Label l2 = new Label(); 
      mv.visitLabel(l2); 

      if (throwing) { 
       // more Aggressive: throw exception 
       mv.visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException"); 
       mv.visitInsn(Opcodes.DUP); 
       mv.visitLdcInsn("Swing Component called from outside the EDT"); 
       mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V"); 
       mv.visitInsn(Opcodes.ATHROW); 

      } else { 
       // this just dumps the Stack Trace 
       mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Thread", "dumpStack", "()V"); 
      } 
      mv.visitLabel(l1); 
     } 
    } 
} 
+0

我是java代理的新手。你能否給出更詳細的指導說明如何在現有項目中使用Swing EDTCheckAgent類? – peterboston

+0

我更新了答案。它已經描述瞭如何打包它,現在我添加了-javaagent:選項。爲它創建一個github項目可能是有意義的。你怎麼看?有興趣這樣做嗎? – ruediste

+0

感謝您的更新。我實際上設法使它適用於我的應用程序。但似乎從我的應用程序中找不到任何錯誤。使用RepaintManager,會報告多項違規行爲,並且我檢查了結果是違規行爲。 – peterboston

相關問題