AWT 线程问题
听众和线程
除非另有说明,否则所有 AWT 监听都会在事件分派线程上得到通知。在调度期间从任何线程删除/添加监听是安全的,但更改只会影响后续通知。例如,如果从另一个关键监听器添加了一个关键监听器,则新添加的监听器只会在后续的关键事件上得到通知。
自动关机
根据Java 虚拟机规范在第 2.17.9 和 2.19 节中,Java 虚拟机 (JVM) 最初以单个非守护线程启动,该线程通常调用某个类的main 方法。当发生以下两种情况之一时,虚拟机将终止其所有活动并退出:
- 所有不是守护线程的线程都会终止。
- 某个线程调用类
Runtime或类System的exit方法,安全管理器允许退出操作。
这意味着如果应用程序本身不启动任何线程,JVM 将在 main 终止后立即退出。然而,对于创建并显示 java.awt.Frame 的简单应用程序,情况并非如此:
public static void main(String[] args) {
Frame frame = new Frame();
frame.setVisible(true);
}
原因是 AWT 封装了异步事件分派机制来处理 AWT 或 Swing 组件可以触发的事件。该机器的确切行为取决于实现。特别是,它可以出于内部目的启动非守护程序辅助线程。事实上,这些是阻止上述示例退出的线程。对该机器的行为施加的唯一限制如下:
EventQueue.isDispatchThread返回true当且仅当调用线程是由机器启动的事件调度线程;AWTEvents实际上被排队到一个特定的EventQueue(请注意,可以合并发布到EventQueue的事件):-
- 按顺序。
- 也就是说,不允许同时调度来自该队列的多个事件。
-
- 与他们入队的顺序相同。
-
也就是说,如果
AWTEventA 在AWTEventB 之前入队到EventQueue,那么事件 B 将不会在事件 A 之前被分派。
-
- 当应用程序中至少有一个可显示的 AWT 或 Swing 组件时,至少有一个活动的非守护线程(参见
Component.isDisplayable)。
- 如果某个线程调用类
Runtime或类System的exit方法,无论是否存在可显示组件,JVM 都会退出; - 即使应用程序终止了它启动的所有非守护线程,只要至少有一个可显示组件,JVM 也不会退出。
依赖于实现的行为。
当以下三个条件为真时,AWT 将终止其所有辅助线程,从而允许应用程序干净地退出:- 没有可显示的 AWT 或 Swing 组件。
- 本机事件队列中没有本机事件。
- java EventQueues 中没有 AWT 事件。
System.exit 的情况下干净退出的独立 AWT 应用程序必须:
- 确保在应用程序完成时所有 AWT 或 Swing 组件都不可显示。这可以通过在所有顶级
Windows上调用Window.dispose来完成。参见Frame.getFrames。 - 确保应用程序使用任何 AWT 或 Swing 组件注册的 AWT 事件监听器的任何方法都不会陷入无限循环或无限期挂起。例如,由某个 AWT 事件触发的 AWT 监听器方法可以将相同类型的新 AWT 事件发布到
EventQueue。争论的焦点是 AWT 事件监听器的方法通常在辅助线程上执行。
- 其他包可以为内部需要创建可显示的组件,并且永远不会使它们不可显示。
- Microsoft Windows 和 X11 都允许应用程序将本机事件发送到属于另一个应用程序的窗口。有了这个特性,就可以编写一个恶意程序,它会不断地向所有可用的窗口发送事件,从而阻止任何 AWT 应用程序干净地退出。
<...>
Runnable r = new Runnable() {
public void run() {
Object o = new Object();
try {
synchronized (o) {
o.wait();
}
} catch (InterruptedException ie) {
}
}
};
Thread t = new Thread(r);
t.setDaemon(false);
t.start();
<...>
Java 虚拟机规范保证在该线程终止之前 JVM 不会退出。