6

EDT を正しく使用するための多くのチュートリアルと例を見つけましたが、逆にどのようにすればよいかを聞きたいと思います: Swing GUI と長いネットワーク操作を伴う多くの機能を備えた複雑なアプリケーションをチェックし、EDT の場所を見つけます。不適切な使用。

私はそれを発見しました

SwingUtilities.isEventDispatchThread()

コードの一部が EDT 内にあるかどうかを確認するために使用できるので、すべての長い操作がたまたま SwingUtilities.isEventDispatchThread() が true を返す場所内にないことを確認できました。

そうですか?EDT の誤った使用を求めて、アプリケーション全体をデバッグするために何か良い方法はありますか? ありがとうございました。

4

2 に答える 2

7

そうですか?

はい、の値を確認することはSwingUtilities.isEventDispatchThread()、コードがイベント ディスパッチ スレッド (EDT) 上にあるかどうかを確認する 1 つの方法です。

別の方法は、表示または印刷することThread.currentThread().getName()です。ほとんどの場合、EDT の名前は「AWT-EventQueue-0」です。

この気の利いたコードは、 Swing のデバッグ、最終要約の記事からのものです。ただし、完全な 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();
        }
    }
}
于 2013-07-20T09:57:08.823 に答える
3

アプリケーション全体の EDT が正しく使用されていることを確認する 1 つの方法は、Java エージェントを使用することです。以下のコードは、最後の概要である Debugging Swing の下に掲載されたエージェントの改良版です。ASM 4.1 で動作します。asm-all-4.1.jar (解凍済み)、コンパイル済みコード、およびエージェントを Premain-Class として指定するマニフェストを含む Jar を作成し、開始します。

/**
 * 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);
        }
    }
}
于 2013-09-13T13:24:18.407 に答える