【问题标题】:What can cause the EDT not to (re)start什么会导致 EDT 不(重新)启动
【发布时间】:2015-09-22 08:29:25
【问题描述】:

我正在寻找导致 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 上已经安排了一些Runnables,并且所有这些都成功运行,否则测试不会达到这一点。这表明 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 或任何其他线程上设置特殊的 UncaughtExceptionHandlers,也没有替换默认的 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 组件,因为它们从未变得可见。
  • 我想在EDT 上执行的不同Runnables 是使用invokeAndWait 安排的。在这期间,由于没有可见的顶部组件和未决事件,EDT 完全有可能关闭,并且下一个 Runnable 在“新”EDT 上执行。

但据我所知,这并不违反任何与 Swing 相关的并发规则。我将所有操作安排在正确的线程上,并且规则中没有提到您需要在程序运行期间始终保持顶级组件可见以保持相同的 EDT 处于活动状态。 EDT 被替换的事实(这也可能由于异常 as discussed here 而发生)应该 AFAIK 不会阻止任何后续的 EventQueue#invokeAndWait 调用。

【问题讨论】:

  • 好,但是怎么样,什么时候,顶级容器至少有一次(真正)可见......,因为没有这一步就无法以某种方式测试,一切
  • @mKorbel 不太清楚你的意思,但在那个特定的测试中,我没有可见的顶级组件。我有 Swing 组件,但从不将它们放在顶级组件中,也不让它们可见
  • @mKorbel 我试着澄清这个问题,因为我仍然不能 100% 确定你的答案与我的问题有什么关系

标签: java swing testing event-dispatch-thread


【解决方案1】:

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

@罗宾

  • 那么就无法测试 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

【讨论】:

  • 不太确定我是否理解这个答案。您的代码按原样运行当然会输出它不在 EDT 上。为什么会这样?当然,如果我取消注释所有内容,框架仍然可见并且 JVM 不会退出。我的问题是我从主线程调用EventQueue.invokeAndWait,它只是卡在那里,因为它无法启动 EDT。在我看来,这与可见或不可见的顶级组件无关,因为要使第一个顶级组件可见,我必须执行完全相同的调用
  • @Robin AFAIK EventDispatchThread 可以启动、完成、使用,然后与一些必须可见的 JComponent 一起测试,此规则在 AWT / Swing API 中实现,简单的 JFrame.setVisible(true) 启动EDT,在当前 JVM 实例处于活动状态之前,不可能在逻辑上终止(GC,任何东西 == 缺少 finalize()),使用 SwingWorker 存在许多滞后,即在没有可见 JComponent 的情况下使用,否则您可以通过肮脏的黑客,通过打破这个规则,覆盖顶级容器的 isVisible,注意我从来没有见过稳定的测试代码
  • 我意识到您需要一个可见的顶级组件来保持 EDT 活动。但我不在乎它是否还活着。我只想能够从另一个线程调用EventQueue.invokeAndWait 在 EDT 上运行某些东西。这应该得到支持,否则你永远无法让你的第一个顶级组件可见。在我的测试中,JVM 仍然活着,因为我的主线程仍在运行,所以不需要 EDT 来保持我的 JVM 活着。我只想在 EDT 上运行测试的某些部分,因为它们与不可见的 Swing 组件交互。我仍然没有看到我做错了什么。
  • @Robin 1. 看起来像你(可能是我的代码中描述不佳:-)不明白 EDT 是如何工作的 - 空的 queueEventQueue.isDispa... 返回 false,然后是 2 . 是否对仅在 Swing API 中实现的方法调用 invokeLater 无关紧要,invokeAndWait required false,否则 RepaintManager 将听到异常,3. 指定 EDT(所有 AWT/Swing 方法,包括 SwingWorker),必需(in)可见的 GUI,4. EDT 必须存在于 JVM 中,然后才能调用(不)可见的 GUI,5.invokeLater / invokeAndWait 与 AWT/Swing (J)Components 一起创建 EDT
  • @Robin 6. 你不能正确地使用 invokeLater / invokeAndWait,7. 澄清我的第一点。在所有方法都被执行到 AWT/Swing 并且它的(错误使用的)局部变量被 finalize()'d(准备 GC 或已经 GC'ed)之后,EventQueue 从 true 切换到 false(5-30 秒,取决于 JavaSE 方法, 避免链接到未关闭的 FileIO、JDBC、Socked 等。在 Java7 和更新版本中很好解决)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多