user13433174
user13433174

Reputation:

If EDT is a separate thread, why does invokeLater wait for the main thread to finish in this example?

So if Event Dispatch Thread is a separate thread from the main thread, that makes me think the next code would output

Before
Runnable
true
After

But when i run it, it's as if the EDT waits for the main thread to finish before running the chunk of code inside the invokeLater(..) method. And the output is:

Before
After
Runnable
true

The code:

public class Main {
    public static void main(String[] args) {
        System.out.println("Before");
        SwingUtilities.invokeLater(() -> {
            System.out.println("Runnable");
            System.out.println(SwingUtilities.isEventDispatchThread());
        });

        System.out.println("After");
    }
}

However, if i replace invokeLater(..) with invokeAndWait(..), then i get

Before
Runnable
true
After

This makes me think the EDT is not really a separate thread, or at least in this example it behaves or just looks to me like it isn't. How do you explain this?

Upvotes: 4

Views: 555

Answers (3)

DaveB
DaveB

Reputation: 2163

In truth, your initial assumption is incorrect. The only thing we can assume from your code is that "Before" will be output before anything else, and that "Runnable" will be output before "true".

The execution of the EDT code will happen when it happens, and there are no guarantees of when that will be in relationship to your other code.

In reality, it's very rare that the EDT code would actually execute before the "After" code would execute. There's just too much overhead in submitting a job to the EDT and having it picked up and run before a single line of code in your main thread can run. So I would have expected the output that you actually saw.

The really big take-away here is that you should NEVER assume any order of operations between threads unless you specifically engineer it, and you should never do that to the EDT.

Upvotes: 0

rzwitserloot
rzwitserloot

Reputation: 103893

How do you explain this?

The same explanation that applies to just about every question when you attempt to explain observed behaviour where threading is involved:

Threads are complicated beasts, generally adding code to observe what is happening will affect the threading, threading as a general concept is not repeatable (things depend on which song is playing in your music player to the phase of the moon), and it doesn't work the way you think it does, and the explanations and mental models provided are oversimplifications that do not hold up.

It's not too hard to explain here, fortunately:

  1. Yes, it really is a separate thread. You can always run Thread.currentThread() to see this.

  2. Threads aren't some voodoo magic instantaneous thing, and your CPU isn't secretly a batch of entirely independent multitude of computers.

The invokeLater call takes your code, adds it to a queue, and pings the EDT to wake up and process that queue, which now has 1 item in it. This queue gets lots of traffic; when you move another window over yours, the EDT gets pings to repaint. When you click a button, that gets added to the queue too, etc.

Regardless of that traffic on that queue, the act of waking up a thread is not quite instant. Your main thread gets around to writing After much faster (not that a mere human can observe it, computers run a few million times faster than what we can observe with our eyeballs) than your computer system gets around to actually waking up that thread.

Lob a bomb in there to see the effects. For example, add Thread.sleep(1000); right before sysout("After"); and observe what happens.

Of course you observe what you did when you use invokeAndWait - that will do the exact same thing (add your code to the queue, and send a ping to the thread to go wake up), but it will then freeze your main thread. The code that got shipped off to the EDT will update some internal boolean ("Yup, I have run to the end", and send a ping back to the main thread to wake up, check that boolean, and keep going). Hence, it cant continue until your code (that prints Runnable, and true) has run.

In other words:

Your second snippet always outputs what you wrote.

Your first snippet will output, well, who knows. The system is free to print B/R/t/A, or B/A/R/t, or even B/R/A/t in rare cases. As an additional problem, System.out is heavy and causes synchronization, so if you are printing whilst analysing threads, the stuff you observe is generally completely irrelevant.

Upvotes: 6

Andy Turner
Andy Turner

Reputation: 140544

It is a separate thread, you're just asking the current thread to invoke the code on the EDT and wait until it has been executed.

It's just like starting a thread explicitly:

Thread t = new Thread(() -> { ... });
t.start();

and then waiting for it to complete:

t.join();

Upvotes: 3

Related Questions