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();
    }
});
Community
  • 1
  • 1
eagertoLearn
  • 8,372
  • 22
  • 66
  • 108
  • 1
    What confuses you about it? – Radiodef Mar 10 '14 at 22:58
  • 1
    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. – Boris the Spider Mar 10 '14 at 22:59
  • 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? – fdreger Mar 10 '14 at 23:00
  • @BoristheSpider: Since `EDT` is a thread, I thought it should have a `run` method – eagertoLearn Mar 10 '14 at 23:00
  • 2
    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. – JB Nizet Mar 10 '14 at 23:01
  • 2
    Read about the [Producer/Consumer pattern](http://java.dzone.com/articles/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**. – Boris the Spider Mar 10 '14 at 23:03
  • @BoristheSpider Exactly; you should post that as part of an answer. – Jason C Mar 10 '14 at 23:04
  • so what would happen if I call `SwingUtilies.invokeLater` right below the call, as I shown in the edit above? – eagertoLearn Mar 10 '14 at 23:09
  • What do _you_ think will happen given what I have said? – Boris the Spider Mar 10 '14 at 23:10

2 Answers2

13

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.

Radiodef
  • 35,285
  • 14
  • 78
  • 114
  • 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? – eagertoLearn Mar 10 '14 at 23:20
  • 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. – Radiodef Mar 10 '14 at 23:26
  • two completely independent GUI added to same EDT will not cross-talk with each other right? – eagertoLearn Mar 10 '14 at 23:28
  • 1
    @eagertoLearn _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. – Boris the Spider Mar 10 '14 at 23:34
  • @eagertoLearn I'm not sure what you mean. The EDT only processes one event at a time. – Radiodef Mar 10 '14 at 23:43
  • @Radiodef: yes, that really helped. is that why it is said `invokeLater` is asynchronous but `invokeAndWait` is synchronous? – eagertoLearn Mar 11 '14 at 00:53
  • `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. – Radiodef Mar 11 '14 at 01:02
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.

Jason C
  • 34,234
  • 12
  • 103
  • 151