I recently found an example code:
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
The createAndShowGUI() method opens a user interface window. Then I tried to trim the code as the following:
public static void main(String[] args) {
createAndShowGUI();
}
Both versions work equally well. What is the difference?
99% of the time either code will work.
However, Swing was designed such that all updates to Swing components should be done on the Event Dispatch Thread (EDT). Read the Swing tutorial on Concurrency for more information.
The problem is the 1% of the time when it might not work. You don't want to waste time trying to debug random problems.
SwingUtilities.invokeLater ensures that the code executes on the Event Dispatch Thread (EDT). Swing is single-threaded, all component instantiation and display should happen on the EDT. Things might seem to work otherwise, but you'll possibly run into issues.
Swing has single threaded Event Loop based design and is not thread safe, with only thread safe part being a set of invokeXXX() methods used to transfer control to the Swing event loop.
Your "broken" code works just fine because nothing else is trying to touch the same part of Swing data structures from within the Event Loop at the same time.
The "main" thread started by the JVM is not the Event Dispatch Thread.
from The Event Dispatch Thread
Some Swing component methods are labelled "thread safe" in the API specification; these can be safely invoked from any thread. All other Swing component methods must be invoked from the event dispatch thread. Programs that ignore this rule may function correctly most of the time, but are subject to unpredictable errors that are difficult to reproduce.
Related
I have three questions which are closely related in that they are born out of each other and represent a train of thought, so I am posting them under one question. It would not help me construct the big picture of my question if I posted them separately.
1) Could you please explain in simple language what SwingUtilities.invokeLater does? I understand threading, dare I say quite a bit, but still the language of the documentation confuses me. It says:
Causes doRun.run() to be executed asynchronously on the
AWT event dispatching thread. This will happen after all
pending AWT events have been processed. This method should
be used when an application thread needs to update the GUI.
In the following example the invokeLater call queues
the Runnable object doHelloWorld
on the event dispatching thread and
then prints a message.
If I put some effort in to make sense of what that says, I think here is what it says, but I couldn't be so sure about it. I think it says:
The invokeLater method schedules the main window creation and the setting up of its dispatcher / message pump on the primary thread of the application only and not on a separate thread. It does it by posting the message to create the window and set it up on the main / primary application thread. In other words, the main thread is saying to us, "The window you are asking me to create will be created after I am done doing everything else that is on my plate right now."
But then two things confuses me, which I list as the two questions below.
2) Then why do I need to implement the new window's message loop as a Runnable. This implies that I want a separate thread to execute that message loop.
3) I printed out the current thread Id's in the function that creates the window and the function that is the window's message loop, and they are both different threads. So, each window in Swing runs on its own thread? That is insane. Can you please explain to me what is happening here? And also if you could please explain in a paragraph or two the threading model of GUI applications created in Swing?
public static void main(String[] args) {
SwingUtilities.invokeLater(new MainWindowEventLoop());
System.out.println(String.format("Main thread %1$d started.",
Thread.currentThread().getId()));
}
public class MainWindowEventLoop implements Runnable {
#Override
public void run() {
JFrame mainWindow = new MainWindow("Main Window");
System.out.println(String.format("Main window loop running on thread %1$d.",
Thread.currentThread().getId()));
}
}
Output:
Main thread 1 started.
Main window loop running on thread 14.
It's a little complicated, but Swing is not thread safe. To run the GUI asynchronously and safely, Sun/Oracle uses a locking pattern called Ad-Hoc Thread Confinement. All Swing components must run on the AWT EDT (Event Dispatch Thread) or the result is not thread safe.
Here's a link to Oracle's tutorial. Try to read all of those sections, see if it makes more sense.
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/
Each window does NOT run on its own separate thread. There is only one EDT. Each windows runs on the SAME thread, the EDT. Each Runnable you send to the EDT is executed sequentially, one after the other, when the EDT has the opportunity to do so. Hence the "later" part of invokeLater().
Basically all the swing windows are bounded to main thread. Every single component in swing runs as thread. After the completion of an event control again returns back to main thread which waits for an event to occur.
I'm totally fresh about threading and GUIs, therefore I couldn't figure out exactly where to call this EventQueue.invokeLater() method.
Am I supposed to call it in every event listeners and something else? What are those "things" to call this method? If so, is there any alternative way to call-once-apply-everywhere method so that It won't take bunch of lines to tuck them to the Event dispatch thread?
Thank you.
therefore I couldn't figure out exactly where to call this EventQueue.invokeLater() method.
Swing components need to be updated on the EDT so you would only use invokeLater(...) if you have code executing in a separate Thread and you want to update a GUI component.
Read the section from the Swing tutorial on Concurrency for more information.
As a general rule, unless you are using Threads, you only need to use this method when you create your GUI. Take a look at the FrameDemo from the section in the Swing tutorial on How to Make Frames for a simple example to get you started.
Am I supposed to call it in every event listeners?
No!
All code in an event handler already executes on the Event Dispatch Thread (EDT)so you don't need to invoke this method.
Swing is not thread safe. Which means that all interactions with Swing objects should be done via the event thread. Swing does this internally as well, so any time Swing calls an event listener, this will be done on the event thread.
This means two things, firstly, if you ever need to interact with a Swing object, your code should be invoked on the event dispatcher thread.
Also, it means that if you have any code in your event listeners that will run for any noticeable period of time, should be invoked on another thread from your listeners. If you do not do this, then your UI will appear frozen. A SwingWorker object can help with this.
To answer your question about needing to check EventQueue.isDispatchThread() that you asked in the some of existing answers' comments:
No, you don't have to check if you are already on the EDT before calling invokeLater().
The SwingUtilities JavaDoc states:
If invokeLater is called from the event dispatching thread -- for example, from a JButton's ActionListener -- the doRun.run() will still be deferred until all pending events have been processed. Note that if the doRun.run() throws an uncaught exception the event dispatching thread will unwind (not the current thread).
Below is further explanation of invokeLater() use.
Notice in ButtonDemo.java that createAndShowGUI() is called using invokeLater(). We must do this because the main() method is not running on the EDT (Event Dispatch Thread). main() is running on its own special main thread that every Java app has.
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
However in actionPerformed(), the invokeLater() method is not used because here we already on the EDT.
//b1, b2, and b3 are all JButtons
public void actionPerformed(ActionEvent e) {
if ("disable".equals(e.getActionCommand())) {
b2.setEnabled(false);
b1.setEnabled(false);
b3.setEnabled(true);
} else {
b2.setEnabled(true);
b1.setEnabled(true);
b3.setEnabled(false);
}
}
As far as I can recall, Event Listener methods such as actionPerformed() are always called from the EDT. (Sorry I don't know supporting documentation off-hand, and I don't have enough reputation yet to post anymore links anyway. Along with reading the pages linked in camickr's answer, try Google searching for "java tutorial swing introduction event listeners".)
So if you are trying to update a Swing component from a thread other than the EDT, call invokeLater(). If you are in an Event Listener method (i.e. already on the EDT), you don't need to call invokeLater().
So I have seen countless different GUI tutorials, and all of them have said to use this code:
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
And although I may not quite understand what this exactly does, since I'm somewhat new to GUI, I do understand the basics of what it does... or so I thought. But then I, as an experiment, cut it all out, and just left:
public static void main(String[] args) {
createAndShowGUI();
}
And it appeared to work. So now I have a question: what is the purpose of keeping the first piece of code I had, rather than just using the second, when it seemed like the second worked just as fine? If it is necessary, what will happen if I don't use it?
In short, yes, it is necessary whenever you make changes to any Swing object (unless the API says they are thread safe).
Any changes you make to the GUI must be made on the Event Dispatch Thread (EDT) because the Swing objects are not thread safe. From the Event Dispatch Thread Tutorial
Swing event handling code runs on a special thread known as the event
dispatch thread. Most code that invokes Swing methods also runs on
this thread. This is necessary because most Swing object methods are
not "thread safe": invoking them from multiple threads risks thread
interference or memory consistency errors. Some Swing component
methods are labelled "thread safe" in the API specification; these can
be safely invoked from any thread. All other Swing component methods
must be invoked from the event dispatch thread. Programs that ignore
this rule may function correctly most of the time, but are subject to
unpredictable errors that are difficult to reproduce.
Calling SwingUtilities.invokeLater ensures that the code int he runnable is invoked on the EDT and you don't get weird errors. That is why when you removed that code it looks like everything is working, because in the situations you tested it probably did. But it might not always and you don't want to have code that works sometimes or has timing issues.
I was looking at some example code of GUIs in Java, and I was wondering what the proper way to display a GUI. Suppose a createAndShowGUI() method is written for some GUI. I saw something like this:
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
Would it be wrong simply to call createAndShowGUI() at the end of the main method without the javax.swing stuff? Or I guess my real question is about what is going on here. I'm familiar with threads but I am not sure why it's necessary to make a new thread (is that what's going on here?) to display the GUI.
All interactions with the UI (Swing or AWT) MUST be executed from within the context of the Event Dispatching Thread.
Swing (and AWT) components are not thread safe, changing any of them from any thread other the EDT can lead to corrupted updates, paint artifices, dead locks and possibly crash the VM. They are also notoriously difficult to debug.
You might like to have a read through
The Event Dispatch Thread
The Single Thread Rule in Swing
Will the real Swing Single Threading Rule please stand up?
I should also add, when the main method is executed, it is running in what ever thread the VM created for it. This is guaranteed not to be the EDT, as it will not have begin started until it is needed.
This is why good Swing programs always start with something like EventQueue.invokeLater. You could also use SwingUtilities.invokeLater, but it's generally the same thing.
Because everything related to a GUI should be done through the Event Dispatch Thread (EDT), that is how Java manages the whole drawing of interfaces. Basically the method delegates the execution of the run() method of the passed Runnable object to the correct thread.
Mind that Runnable is not a Thread, it's just an interface to provide a method that does something (hence the class is runnable). But there is no thread involved here, the fact that Thread extends from Runnable is just because a thread is also a Runnable object in the sense that can execute something.
Please explain invokeAndWait() method in SwingUtilities.I am unable to understand this.
Explain it very clearly. It would be of great help if you try out with an example.
Edited to add #noob's expansion of the question:
What's not clear about this?
Here's a modified usage example:
import javax.swing.SwingUtilities;
public class InvokeAndWaitStuff
{
public static void main(String[] args)
{
final Runnable doHelloWorld = new Runnable() {
public void run() {
System.out.println("Hello World on " + Thread.currentThread());
}
};
Thread appThread = new Thread() {
public void run() {
try {
SwingUtilities.invokeAndWait(doHelloWorld);
}
catch (Exception e) {
e.printStackTrace();
}
System.out.println("Finished on " + Thread.currentThread());
}
};
appThread.start();
}
}
Output:
Hello World on Thread[AWT-EventQueue-0,6,main]
Finished on Thread[Thread-0,5,main]
And why is this important?:
Causes doHelloWorld.run() to be
executed synchronously on the AWT
event dispatching thread. This call
blocks until all pending AWT events
have been processed and (then)
doHelloWorld.run() returns. This
method should be used when an
application thread needs to update the
GUI.
As far as I can tell, this is basically a bottleneck that forces GUI updates to be executed synchronously by a single thread, rather than asynchronously by multiple threads, which can potentially be unsafe.
To understand what invokeAndWait() does, you first need to understand the event/thread model of Swing.
Basically, everything that affects the GUI in any way must happen on a single thread. This is because experience shows that a multi-threaded GUI is impossible to get right.
In Swing, this special GUI thread is called the Event Dispatch Thread, or EDT. It is started as soon as a Swing top-level component is displayed, and it's bascially a worker thread that has a FIFO queue of event objects that it executes one after another.
When a Swing GUI needs to be drawn or updated, the JRE places an event on the EDT queue. User actions that cause listeners to be called start as events on the EDT queue. And (this is this is the important part) everything your program does that changes the GUI (like registering listeners, adding/removing GUI components or changing model data that the GUI displays) must be placed in the EDT queue, or the GUI can get corrupted.
And now for the finish: invokeAndWait() places the Runnable you pass to it into the EDT event queue and waits until the EDT has executed it. This should be used when a non-GUI thread needs to do something that affects the GUI, but also needs to wait until it is actually done before it can continue. If you just want to do something that affects the GUI but do not care when it is finished, you should instead use invokeLater().
I had a similar problem in a JTable. The program was blocked somewhere in "scrollRectToVisible" method. I have replaced the call by wrapping it in an invokeLater call.
The invokeAndWait did not resolve my block problem.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
table.scrollRectToVisible(r);
}
});