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

2

7

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();
    }
});
Albertinaalbertine answered 10/3, 2014 at 22:56 Comment(9)
What confuses you about it?Kinlaw
What is the run method of EDT? Where did you see it? In java the only way to pass around "chunks of code" is inside classes. The "chunk of code" in the Runnable.run method gets invoked at some point later.Putnam
The question does not make sense. You seem to grasp the basic idea: EDT runs in a loop and executes small pieces of code, mostly to react to events. By using invokeLater you pass a Runnable and EDT calls its run method. You ask about a "direct" way - how more direct can you get? how would you imagine the "direct" api?Passer
@BoristheSpider: Since EDT is a thread, I thought it should have a run methodAlbertinaalbertine
The EDT is not a thread that you start yourself. It starts automatically, and you use invokeLater() to pass a task (the Runnable) to run in this thread.Marotta
Read about the Producer/Consumer pattern. The EDT is an implementation of such. Tasks are issued from everywhere (other threads and the EDT) to the EDT's work queue. The EDT takes tasks of the queue (one at a time) and runs them. You cannot pass your code to the run method because the EDT it already running and there is only one.Putnam
@BoristheSpider Exactly; you should post that as part of an answer.Susurration
so what would happen if I call SwingUtilies.invokeLater right below the call, as I shown in the edit above?Albertinaalbertine
What do you think will happen given what I have said?Putnam
K
14

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.

Kinlaw answered 10/3, 2014 at 23:11 Comment(7)
so once the createAndShowGUI is executed, events associated with the operations on GUI components, will again be added to this queue? but we have not specified EDT to do that. In otherwords, when I created a JButton I did not call SwingUtilies.invokeLater to add it to container?Albertinaalbertine
Yes, anything inside createAndShowGUI is now run on the EDT. If any actions inside createAndShowGUI end up queuing new events, the new events are processed once the Runnable from the InvocationEvent is done.Kinlaw
two completely independent GUI added to same EDT will not cross-talk with each other right?Albertinaalbertine
@Albertinaalbertine cross-talk? I think you need to read about threading concepts in Java. Your questions should a lack of understanding of the fundamentals of the topic. Once you have understood how threading works you will be better able to understand how multithreading in Swing works.Putnam
@Albertinaalbertine I'm not sure what you mean. The EDT only processes one event at a time.Kinlaw
@Radiodef: yes, that really helped. is that why it is said invokeLater is asynchronous but invokeAndWait is synchronous?Albertinaalbertine
invokeLater is asynch because the thread making the call to it may continue on past the invokeLater call before the event gets processed and run gets called. invokeAndWait is synch because it waits until run is completed before the calling thread continues.Kinlaw
S
6

You can use SwingUtilities.invokeLater() from any thread to queue a task to be run on the EDT. This is useful in a few situations, most notably in multi-threaded applications as a convenient way to update GUI elements from non-GUI threads, and also when starting your application (which your example hints at) from main(), which is run in the main thread, not the EDT.

E.g.:

// main is not executed in the EDT
public static void main (String[] args) {

    // create and initialize GUI in the EDT to avoid potential thread-safety issues.
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
    });

}

You may wish to have a look at the official tutorial on The Event Dispatch Thread, which should clarify some of your misconceptions about what the EDT is and how you access it.

Susurration answered 10/3, 2014 at 23:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.