Why to use SwingUtilities.invokeLater in main method?
The docs explain why. From Initial Threads
Why does not the initial thread simply create the GUI itself? Because almost all code that creates or interacts with Swing components must run on the event dispatch thread.
and 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.
Using SwingUtilities.invokeLater() in main method
The purpose of the code fragment that you show is to create the Swing UI and the model and to connect them together.
There is no Swing update (in terms of reacting to user input) since there cannot be any user input before the run()
methods ends.
While you could split these tasks between main thread and EDT (and possibly gain a few milliseconds until the UI is first shown) it would also complicate the design of the application (multithreading is no easy topic) and litter the code base with invokeLater()
calls. I would not do it until someone proves it to be necessary.
IMHO the EDT is the main thread in any GUI application. Every reaction to user input starts in this thread and every update of the UI must be done in this thread.
Long running tasks should be done in a background thread - which usually means anything that takes more than a few milliseconds.
What if creating the model takes several seconds?
In that case I would try to split the model creation into two parts:
- create the minimal part that is needed so that the UI can be shown. This should be done in the EDT (because the user has to wait for the completion of this part anyway - before the UI is shown he cannot interact with it)
- do the remaining, long running parts in a background thread.
What if this cannot be done? (i.e. the UI cannot be displayed until the model is fully initialized)
In this case the user has to wait for the complete initialization of the model before he can see and use the UI anyway. So it doesn't matter whether this initialization runs on the EDT or the main thread. So use the simpler solution: everything on the EDT.
But give the user some hint that your application is starting by showing a splash screen
SwingUtilities.invokeLater() why is it needed?
Swing objects are not thread safe. SwingUtilities.invokeLater()
allows a task to be executed at some later point in time, as the name suggests; but more importantly, the task will be executed on the AWT event dispatch thread. When using invokeLater
, the task is executed asynchronously; there's also invokeAndWait
, which won't return until the task has finished executing.
Some information about the decision not to make Swing thread-safe can be found here: Multithreaded toolkits: A failed dream? [Archived]
What exactly does the call to the SwingUtilities.invokeLater() method into the main class that perform a Swing application?
This is really two questions rolled into one.
SwingUtilities.invokeLater()
makes sure that whatever you want to do happens on the Event Dispatcher Thread. Since Swing is strictly single threaded, this is required for everything that interacts with Swing and doesn't already run in the EDT (e.g. is called as a result of an event handler) Themain()
method of your application is one such example, as it's invoked by the VM on startup in its own separate thread (called, unsurprisingly, the Main Thread).- This is an anonymous inner class. It's a shortcut for creating a class that implements
Runnable
and creating an instance of that class straight away. (Note that it isn't completely equivalent with doing that, there are small but important details in which it is different.)SwingUtilities.invokeLater()
will then put thisRunnable
object in the Swing event queue, which will be processed by the EDT (usually a couple of moments) later, calling therun()
method you provided.
What does SwingUtilities.invokeLater do?
As other answers have said, it executes your Runnable
on the AWT event-dispatching thread. But why would you want to do that? Because the Swing data structures aren't thread-safe, so to provide programmers with an easily-achievable way of preventing concurrent access to them, the Swing designers laid down the rule that all code that accesses them must run on the same thread. That happens automatically for event-handling and display maintenance code, but if you've initiated a long-running action - on a new thread, of course - how can you signal its progress or completion? You have to modify a Swing control, and you have to do it from the event-dispatching thread. Hence invokeLater
.
Why does invokeLater execute in the main thread?
To me it seems like a misunderstanding on your side
The first line is like saying: "Ok, Swing, what I want you to invokeLater
is someMethod().toString()
". So Swing executes it
The second line is like saying: "Ok, Swing, what I want you to invokeLater
is the method toString()
of the object returned by the method someMethod()
". A someMethod()
method that I am executing right now
So the result is completely logical to me
Just keep in mind that before evaluating a function (in this case invokeLater
) Java needs to evaluate all arguments. So in the first case Java evaluate a lambda function (no need to execute it) and in the second case it encounters a method invocation so it needs to execute it
What to put (and not to put) inside SwingUtilities.invokeLater?
For future comers, I figured it out, even though the docs don't really explain this in a way that is obvious to Swing newbies:
- When the docs or blogs/articles/people say "Anything that touches the UI", this translates to: "Any manipulation of a Swing class under
javax.swing.*
". Hence, doing anything withJFrame
,JPanel
, or any of theJComponents
. All this stuff should be executed inside theRunnable
you pass toSwingUtilities.invokeLater
from inside your app'smain
method. So basically you end up with one gigantic, monolithicRunnable
that does all UI code manipulation. Strange and anti-MVC IMHO. - If you have a long running process you should strip all the Swing code out of that process and pass it into your own
SwingWorker
SwingUtilities.invokeLater
Do I have to use each time I need to update the GUI components?
No, not if you're already on the event dispatch thread (EDT) which is always the case when responding to user initiated events such as clicks and selections. (The actionPerformed
methods etc, are always called by the EDT.)
If you're not on the EDT however and want to do GUI updates (if you want to update the GUI from some timer thread, or from some network thread etc), you'll have to schedule the update to be performed by the EDT. That's what this method is for.
Swing is basically thread unsafe. I.e., all interaction with that API needs to be performed on a single thread (the EDT). If you need to do GUI updates from another thread (timer thread, networking thread, ...) you need to use methods such as the one you mentioned (SwingUtilities.invokeLater, SwingUtilities.invokeAndWait, ...).
Animations and SwingUtilities.invokeLater
Here is that self-contained code (good call on posting that, BTW) updated to use a Timer
as suggested by @MadProgrammer. In order to access the x
variable, it was moved into the action listener defined for the timer. In order to access the Timer
from within the action listener, it was moved to being a class attribute. The latter meant it was easier to move the bulk of the code into a constructor for an instance of the object.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Test {
Timer timer;
Test() {
int width = 854;
int height = 480;
String title = "Test";
BufferedImage bufferedImage = new BufferedImage(
width, height, BufferedImage.TYPE_INT_RGB);
JFrame frame = new JFrame();
JPanel panel = new JPanel() {
@Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics2D graphics2D = (Graphics2D) graphics;
// when you have an ImageObserver, may as well use it
//graphics2D.drawImage(bufferedImage, 0, 0, null);
graphics2D.drawImage(bufferedImage, 0, 0, this);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(width,height);
}
};
frame.add(panel);
frame.pack();
frame.setTitle(title);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
int size = height / 3;
ActionListener animationListener = new ActionListener() {
int x = -size;
@Override
public void actionPerformed(ActionEvent e) {
if (x <= width) {
Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();
graphics2D.setColor(Color.RED);
graphics2D.fill(new Rectangle2D.Double(x, 0, size, size));
graphics2D.setColor(Color.GREEN);
graphics2D.fill(new Rectangle2D.Double(x, size, size, size));
graphics2D.setColor(Color.BLUE);
graphics2D.fill(new Rectangle2D.Double(x, 2 * size, size, size));
graphics2D.dispose();
panel.repaint();
++x;
} else {
timer.stop();
frame.dispose();
}
}
};
timer = new Timer(10, animationListener);
timer.start();
}
public static void main(String[] args) {
Runnable r = () -> {
new Test();
};
SwingUtilities.invokeLater(r);
}
}
Related Topics
Data Access Object (Dao) in Java
Is a Java String Really Immutable
What Is the List of Valid @Suppresswarnings Warning Names in Java
Jax-Rs/Jersey How to Customize Error Handling
Specifying Java Version in Maven - Differences Between Properties and Compiler Plugin
Can Constructors Throw Exceptions in Java
Add Context Path to Spring Boot Application
Overriding VS Hiding Java - Confused
Quickest Way to Find Missing Number in an Array of Numbers
How to Choose the Id Generation Strategy When Using JPA and Hibernate
Does a Tcp Socket Connection Have a "Keep Alive"
How to Implement a Db Listener in Java
How to Create Correct JSONarray in Java Using JSONobject
Converting Many 'If Else' Statements to a Cleaner Approach
@Runwith(Mockitojunitrunner.Class) VS Mockitoannotations.Initmocks(This)
Why Can Java Collections Not Directly Store Primitives Types
How to Define a Relative Path in Java
Warning About Ssl Connection When Connecting to MySQL Database