Saturday 15 June 2019

java - SwingUtilities.invokeLater takes a Runnable and runs it on the EDT?



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 calls invokeLater 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

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...