Swingutilities.Invokelater

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.

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, ...).

What is SwingUtilities.invokeLater

Nothing bad will happen if you're updating it from the EDT while following guidelines.

That is...

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.

Source

If that isn't the case, invokeLater() is required.

It schedules a Runnable which will be executed on the EDT (event dispatching thread).

what is SwingUtilities.invokeLater(new Runnable())

You are creating an instance of an anonymous implementation of the Runnable interface and passing it to invokeLater, which will put it on a queue. Another thread, the Event Dispatch Thread, pops Runnables off that queue and invokes their run method. (Note that this is indeed a simplistic explanation and does not correspond 100% with reality.)

In terms of the effect you as the programmer are interested in, this makes the following line of code:

chatWindow.append(text);

to be executed not on the current thread, but on the Event Dispatch Thread (EDT). If you tried to execute it on your current thread, it would result in undefined behavior because Swing is not thread-safe and all GUI operations must happen on the mentioned EDT.

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

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.

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]

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.

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);
}
}

Difference between SwingUtilities.invokeLater and SwingWorker Void, Object ?

SwingUtilities.invokeLater
takes a Runnable and invokoes it in the ui thread later. Usually for short running ui related work.

SwingWorker
runs the main work in a non ui thread - the worker thread. After the long running work is done the done() method is invoked in the ui thread (Event Dispatch Thread).

But the SwingWorker's doInBackground() method can also publish intermediate results by invoking the publish() method. The SwingWorker will than ensure that the results to publish are processed by the Event Dispatch Thread. You can hook in by implementing the process() method.



Related Topics



Leave a reply



Submit