I am confused with the signature of SwingUtilities.invokeLater
. It takes a Runnable
object. Is it this Runnable object that is handed over to the Event Dispatch Thread? Why can't I directly call createAndShowGUI
on the run
method of EDT
(if it is possible)?
I have read articles on SO on how the EDT and invokeLater work, but I am confused with the Runnable
object that is passed.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
And what would happen if I call SwingUtilities.invokeLater
again right below the call?
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
SwingUtilities.invokeLater(new Runnable() {
public void run() {
doSomethingOnTopOfGUI();
}
});
Answer
What is new Runnable() {}?
new Runnable() {
public void run() {
createAndShowGUI();
}
};
This declares an anonymous class and instantiates a new instance of it. It is basically equivalent to this:
class Creator implements Runnable {
public void run() {
createAndShowGUI();
}
}
new Creator();
Before Java 8 lambdas, an anonymous class is a way to do a kind of functional programming. Passing a Runnable to invokeLater
is like passing a function that the EDT can execute later whenever it wants (by calling run
on it).
What does invokeLater do with the Runnable?
Initially, all it does is create an event to wrap it (an InvocationEvent) and put it at the end of a queue.
After queuing the event, invokeLater
notifies the EDT (which wakes up if it was waiting). The EDT's own run method is an infinite loop that processes events. It looks something like this (very paraphrased):
public void run() {
while(true) {
EventQueue eq = getEventQueue();
synchronized(eq) {
while(eq.hasNextEvent()) {
processOneEvent(eq.getNextEvent());
}
try {
eq.wait();
} catch(InterruptedException ie) {}
}
}
}
When the EDT gets to the new InvocationEvent, it calls run
on the Runnable, which calls createAndShowGUI
.
So passing a Runnable to invokeLater
or invokeAndWait
lets you run any code you want on the EDT.
How soon is run called?
In most cases, immediately or almost immediately. The EDT is very responsive. The 'Later' part of 'invokeLater' is just a hint to us that:
run
is executed asynchronously. The thread that callsinvokeLater
may continue past the call before the event is processed.- Other events may be processed first.
Of course invokeAndWait
exists as an alternative if a synchronous call to run
is desired. (Though it should be noted that invokeAndWait
may also cause other events to be processed if they were queued before the new InvocationEvent.)
Asynchronous
SwingUtilities.invokeLater(new Runnable() {
public void run() {
System.out.println("hello");
}
});
System.out.println("world");
This may print
hello
world
and it may print
world
hello
because the calling thread may or may not continue before (or even while) the event is processed. It depends on the system's thread scheduler.
Synchronous
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
System.out.println("hello");
}
});
System.out.println("world");
This will definitely print
hello
world
because the calling thread will wait for run
to complete before it continues (unless an exception is thrown).
invokeAndWait
uses a wait and notify scheme to achieve this. A monitor object is created and given to the InvocationEvent. invokeAndWait
calls wait
after posting the event and the InvocationEvent calls notifyAll
after run
completes on the EDT. The wait and notify scheme is a reason invokeAndWait
cannot be called on the EDT. The EDT does not have a way to stop in the middle of one event to process a different one.
What happens if more than one event is queued?
The EDT processes events one at a time, in the order they are queued and according to their priority. Some events can also get merged (a regular InvocationEvent will not). Most events are normal priority. A PaintEvent (like from calling repaint
) is typically low priority and certain system events are of higher priorities.
In the specific example you've given:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
SwingUtilities.invokeLater(new Runnable() {
public void run() {
doSomethingElse();
}
});
Since they are the same kind of event and the same priority, the doSomethingElse
event will be processed after the createAndShowGUI
event is done.
Similarly, if something like this were done:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
System.out.println("world");
}
});
System.out.println("hello");
}
});
That will print
hello
world
because the world
Runnable gets run after the hello
Runnable completes.
No comments:
Post a Comment