AWT Threading Issues
Listeners and threads
Unless otherwise noted, all AWT listeners are notified on the event dispatch thread. It is safe to remove/add listeners from any thread during dispatching, but the changes only affect subsequent notification.For example, if a key listener is added from another key listener, the newly added listener is only notified on subsequent key events.
Auto-shutdown
According to The Java Virtual Machine Specification, sections 2.17.9 and 2.19, the Java virtual machine (JVM) initially starts up with a single non-daemon thread, which typically calls themain
method of some class.
The virtual machine terminates all its activity and exits when
one of two things happens:
- All the threads that are not daemon threads terminate.
- Some thread invokes the
exit
method of classRuntime
or classSystem
, and the exit operation is permitted by the security manager.
This implies that if an application doesn't start any threads itself,
the JVM will exit as soon as main
terminates.
This is not the case, however, for a simple application
that creates and displays a java.awt.Frame
:
public static void main(String[] args) { Frame frame = new Frame(); frame.setVisible(true); }The reason is that AWT encapsulates asynchronous event dispatch machinery to process events AWT or Swing components can fire. The exact behavior of this machinery is implementation-dependent. In particular, it can start non-daemon helper threads for its internal purposes. In fact, these are the threads that prevent the example above from exiting. The only restrictions imposed on the behavior of this machinery are as follows:
-
EventQueue.isDispatchThread
returnstrue
if and only if the calling thread is the event dispatch thread started by the machinery; -
AWTEvents
which were actually enqueued to a particularEventQueue
(note that events being posted to theEventQueue
can be coalesced) are dispatched:-
- Sequentially.
- That is, it is not permitted that several events from this queue are dispatched simultaneously.
-
- In the same order as they are enqueued.
- That is, if
AWTEvent
A is enqueued to theEventQueue
beforeAWTEvent
B then event B will not be dispatched before event A.
-
- There is at least one alive non-daemon thread while there is at
least one displayable AWT or Swing component within the
application (see
Component.isDisplayable
).
- The JVM will exit if some thread invokes the
exit
method of classRuntime
or classSystem
regardless of the presence of displayable components; - Even if the application terminates all non-daemon threads it started, the JVM will not exit while there is at least one displayable component.
Implementation-dependent behavior.
AWT terminates all its helper threads allowing the application to exit cleanly when the following three conditions are true:- There are no displayable AWT or Swing components.
- There are no native events in the native event queue.
- There are no AWT events in java EventQueues.
System.exit
must:
- Make sure that all AWT or Swing components are made
undisplayable when the application finishes. This can be done
by calling
Window.dispose
on all top-levelWindows
. SeeFrame.getFrames
. - Make sure that no method of AWT event listeners registered by
the application with any AWT or Swing component can run into an
infinite loop or hang indefinitely. For example, an AWT listener
method triggered by some AWT event can post a new AWT event of
the same type to the
EventQueue
. The argument is that methods of AWT event listeners are typically executed on helper threads.
- Other packages can create displayable components for internal needs and never make them undisplayable.
- Both Microsoft Windows and X11 allow an application to send native events to windows that belong to another application. With this feature it is possible to write a malicious program that will continuously send events to all available windows preventing any AWT application from exiting cleanly.
<...> 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(); <...>The Java Virtual Machine Specification guarantees that the JVM doesn't exit until this thread terminates.