【发布时间】: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.err 和System.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