什么可能导致EDT不(重新)启动

时间:2015-09-22 08:29:25

标签: java swing testing event-dispatch-thread

我正在寻找EDT关闭而不是重启的可能原因。 更具体的是,我有一个测试套件,偶尔其中一个测试会因超时而受到影响。线程转储总是非常类似于以下(我从主线程中删除了一些不相关的行):

Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.79-b02 mixed mode):

"Attach Listener" daemon prio=10 tid=0x00007f7c60001000 nid=0x5d0f runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"main-SharedResourceRunner" daemon prio=10 tid=0x00007f7c908e6000 nid=0x5ce6 in Object.wait() [0x00007f7c8416a000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000eed580f0> (a jogamp.opengl.SharedResourceRunner)
    at java.lang.Object.wait(Object.java:503)
    at jogamp.opengl.SharedResourceRunner.run(SharedResourceRunner.java:252)
    - locked <0x00000000eed580f0> (a jogamp.opengl.SharedResourceRunner)
    at java.lang.Thread.run(Thread.java:745)

"AWT-XAWT" daemon prio=10 tid=0x00007f7c9085d000 nid=0x5ce3 runnable [0x00007f7c8456e000]
   java.lang.Thread.State: RUNNABLE
    at sun.awt.X11.XToolkit.waitForEvents(Native Method)
    at sun.awt.X11.XToolkit.run(XToolkit.java:541)
    at sun.awt.X11.XToolkit.run(XToolkit.java:505)
    at java.lang.Thread.run(Thread.java:745)

"Java2D Disposer" daemon prio=10 tid=0x00007f7c90840800 nid=0x5ce2 in Object.wait() [0x00007f7c8466f000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000eee2f878> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
    - locked <0x00000000eee2f878> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
    at sun.java2d.Disposer.run(Disposer.java:145)
    at java.lang.Thread.run(Thread.java:745)

"Service Thread" daemon prio=10 tid=0x00007f7c9009b800 nid=0x5cdc runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" daemon prio=10 tid=0x00007f7c90099800 nid=0x5cdb waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" daemon prio=10 tid=0x00007f7c90096800 nid=0x5cda waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" daemon prio=10 tid=0x00007f7c90094000 nid=0x5cd9 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" daemon prio=10 tid=0x00007f7c90072000 nid=0x5cd8 in Object.wait() [0x00007f7c94e73000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000eee34650> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
    - locked <0x00000000eee34650> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" daemon prio=10 tid=0x00007f7c90070000 nid=0x5cd7 in Object.wait() [0x00007f7c94f74000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000eedcc110> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:503)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
    - locked <0x00000000eedcc110> (a java.lang.ref.Reference$Lock)

"main" prio=10 tid=0x00007f7c9000c000 nid=0x5cd1 in Object.wait() [0x00007f7c99c20000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000eb3adf98> (a java.awt.EventQueue$1AWTInvocationLock)
    at java.lang.Object.wait(Object.java:503)
    at java.awt.EventQueue.invokeAndWait(EventQueue.java:1282)
    - locked <0x00000000eb3adf98> (a java.awt.EventQueue$1AWTInvocationLock)
    at java.awt.EventQueue.invokeAndWait(EventQueue.java:1263)


"VM Thread" prio=10 tid=0x00007f7c9006b800 nid=0x5cd6 runnable 

"GC task thread#0 (ParallelGC)" prio=10 tid=0x00007f7c90022000 nid=0x5cd2 runnable 

"GC task thread#1 (ParallelGC)" prio=10 tid=0x00007f7c90023800 nid=0x5cd3 runnable 

"GC task thread#2 (ParallelGC)" prio=10 tid=0x00007f7c90025800 nid=0x5cd4 runnable 

"GC task thread#3 (ParallelGC)" prio=10 tid=0x00007f7c90027800 nid=0x5cd5 runnable 

"VM Periodic Task Thread" prio=10 tid=0x00007f7c900ae800 nid=0x5cdd waiting on condition 

JNI global references: 297

注意main线程如何使用EventQueue.invokeAndWait在EDT上安排某些事情,但线程转储中没有EDT。并且没有EDT意味着main线程将永远等待,因为Runnable永远不会终止。

在进行线程转储的那一刻,在EDT上已经安排了一些Runnable,这些都已成功运行或测试未达到此点。这表明EDT崩溃了,无法重新启动。

不幸的是,我没有代码可以重现这个问题。我甚至无法在我自己的开发机器上重现这个问题,而这个测试有时会遇到问题。我甚至很难在CI上复制这个,因为它似乎每隔几百次就会超时一次。

我正在寻找可能导致此行为的建议,或者我可以做些什么来调查此问题并稳定我的测试。我最好的猜测是异常导致EDT崩溃,但即便如此,EDT应该恢复并且能够运行下一个Runnable发布到它,如下面的代码所示:

import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;


public class Test {
  public static void main(String[] args) throws InvocationTargetException, InterruptedException {
    try {
      EventQueue.invokeAndWait(new Runnable() {
        @Override
        public void run() {
          throw new RuntimeException("exception");
        }
      });
    } catch (Exception e) {
      //ignore
    }
    EventQueue.invokeAndWait(
        new Runnable() {
          @Override
          public void run() {
            System.out.println("EDT restarted");
          }
        }
    );
    System.out.println("All runnables finished");
  }
}

请注意,我的测试在EDT或任何其他线程上没有设置特殊UncaughtExceptionHandler,也没有替换默认的UncaughtExceptionHandler。我甚至不确定EDT是否有例外。当系统超时时,CI系统会丢失System.errSystem.out输出。我只有线程转储可用。

修改

我担心这个问题不是100%清楚。我的测试代码看起来像

@Test
public void testSomethingWhichInteractsWithTheEDT(){
  doStuffOnMainThread();
  //do or check something on the EDT
  EventQueue.invokeAndWait( new Runnable(){...});
  doMoreStuffOnMainThread();
  //do or check something else on the EDT
  EventQueue.invokeAndWait( new Runnable(){...});
}

偶尔会有其中一个

EventQueue.invokeAndWait( new Runnable(){...});

主线程上的调用将无限期地阻塞,并且在该点进行的线程转储将不再显示"AWT-EventQueue-0"实例。

虽然测试使用Swing组件,但它们永远不会显示,也不会添加到顶级组件中。为简单起见,假设我创建JPanel但从未将其添加到JFrame

这意味着:

  • 无论EDT是否存在,JVM都会保持独立,直到达到测试结束。这是因为主线程一直运行到测试结束。一旦我的测试代码结束,JVM将退出而不必处置任何Swing组件,因为它们从未被显示过。
  • 我要使用Runnable安排我要在EDT上执行的invokeAndWaitRunnable。在这两者之间,EDT很可能因为没有可见的顶级组件而没有待处理的事件而关闭,并且下一个EventQueue#invokeAndWait在&#34; new&#34;上执行。 EDT。

但据我所知,这并不违反任何与Swing相关的并发规则。我在正确的线程上安排所有操作,并且规则中没有提到在程序运行期间始终保持顶级组件可见,以保持相同的EDT存活。 EDT被替换的事实(也可能由于异常as discussed here而发生)AFAIK不会阻止任何后续Node.childNodes次呼叫。

1 个答案:

答案 0 :(得分:2)

  

@mKorbel不确定你的意思,但在那个特定的测试中我没有可见的顶级组件。我有Swing组件,但从未将它们放在顶级组件中,也不让它们可见

@Robin

  • 然后无法测试EDT
  • invokeAndWait应始终针对EventQueue.isDispatchThread / SwingUtilities.isEventDispatchThread进行测试,以避免RepaintManager
  • 的例外情况
  • 相当,我对这个问题感到有些惊讶(请不要激动人心: - )

例如

import java.awt.EventQueue;
import java.awt.TextField;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
import javax.swing.*;

public class IsThereEDT {

    private ScheduledExecutorService scheduler;
    private AccurateScheduledRunnable periodic;
    private ScheduledFuture<?> periodicMonitor;
    private int taskPeriod = 30;
    private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    private Date dateRun;

    public IsThereEDT() {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        periodic = new AccurateScheduledRunnable() {

            private final int ALLOWED_TARDINESS = 200;
            private int countRun = 0;
            private int countCalled = 0;

            @Override
            public void run() {
                countCalled++;
                if (this.getExecutionTime() < ALLOWED_TARDINESS) {
                    countRun++;
                    isThereReallyEDT(); // non on EDT
                }
            }
        };
        periodicMonitor = scheduler.scheduleAtFixedRate(periodic, 0, taskPeriod, TimeUnit.SECONDS);
        periodic.setThreadMonitor(periodicMonitor);
        isThereReallyEDT();
        //TextField text = new TextField();
        //JFrame frame1 = new JFrame("Frame 1");
        /*SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {                
                isThereReallyEDT();
                JFrame frame1 = new JFrame("Frame 1");
                frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame1.getContentPane().add(new JLabel("Hello in frame 1"));
                frame1.pack();
                frame1.setLocation(100, 100);
                frame1.setVisible(true);
            }
        });*/
        /*SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame2 = new JFrame("Frame 2");
                frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame2.getContentPane().add(new JLabel("Hello in frame 2"));
                frame2.pack();
                frame2.setLocation(200, 200);
                frame2.setVisible(true);
                isThereReallyEDT();
            }
        });*/
        isThereReallyEDT();
    }

    private void isThereReallyEDT() {
        dateRun = new java.util.Date();
        System.out.println("                         Time at : " + sdf.format(dateRun));
        if (EventQueue.isDispatchThread()) {
            System.out.println("Calling from EventQueue.isDispatchThread");
        } else {
            System.out.println("There isn't Live EventQueue.isDispatchThread, why any reason for that ");
        }
        if (SwingUtilities.isEventDispatchThread()) {
            System.out.println("Calling from SwingUtilities.isEventDispatchThread");
        } else {
            System.out.println("There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
          IsThereEDT isdt = new IsThereEDT();
        /*SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setVisible(true);
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                }
                frame.dispose();
                JOptionPane.showMessageDialog(frame, "Test case 1, "
                        + "JVM won't terminate.");
            }
        });*/
    }
}

abstract class AccurateScheduledRunnable implements Runnable {

    private ScheduledFuture<?> thisThreadsMonitor;

    public void setThreadMonitor(ScheduledFuture<?> monitor) {
        this.thisThreadsMonitor = monitor;
    }

    protected long getExecutionTime() {
        long delay = -1 * thisThreadsMonitor.getDelay(TimeUnit.MILLISECONDS);
        return delay;
    }
}

具有正确和良好的输出(没有显示顶级容器至少一次(真的)可见到屏幕)

             Time at : 11:03:50 There isn't Live EventQueue.isDispatchThread, why any reason for that  There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that 

             Time at : 11:04:20 There isn't Live EventQueue.isDispatchThread, why any reason for that  There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that 

             Time at : 11:04:50 There isn't Live EventQueue.isDispatchThread, why any reason for that  There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that