java - 什么会导致 EDT 无法(重新)启动

标签 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。我什至不确定美国东部时间是否有异常(exception)。当超时时,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 是否处于 Activity 状态,JVM 都保持 Activity 状态,直到达到我的测试结束为止。这是因为主线程一直运行到测试结束。一旦我的测试代码结束,JVM 将退出而不必处理任何这些 Swing 组件,因为它们从未变得可见。
  • 我想在 EDT 上执行的不同 Runnable 是使用 invokeAndWait 安排的。在此期间,EDT 完全有可能关闭,因为没有可见的顶级组件和未决事件,并且下一个 Runnable 在"new"EDT 上执行。

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

最佳答案

@mKorbel Not really sure what you mean, but in that particular test I have no visible top level components. I have Swing components, but never put them in a top level component nor do I make them visible

@罗宾

  • 那么就不可能测试 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

关于java - 什么会导致 EDT 无法(重新)启动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32712078/

相关文章:

java - 如何使用 Java 驱动程序 (2.0.2,3.1) 和 cassandra 3.7 在 java 中使用 DCAwareRoundRobinPolicy

Java Swing 对动画的支持

java - 从 Maven 运行测试失败并停止构建 jar

java - 克隆多维数组

java - 通过java检索neo4j节点数据时出错

java - 如何在 Java Swing 中使用 vlcj 播放的视频添加搜索栏?

testing - 如何充分发挥手动测试员的作用

覆盖几乎所有物理内存的linux memtester

java - 在javascript中通过selenium登录facebook

java - 如何将 "New folder"按钮添加到 JFileChooser