How does the event dispatch thread work?
events, invoke, java, multithreading, user-interface
Solution
If I understand your question correctly you're wonder why you can't do this:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
showGUI();
}
});
counter.start();
}
The reason why you can't do it is because the scheduler makes no guarantees... just because you invoked `showGUI()` and then you invoked `counter.start()` doesn't mean that the code in `showGUI()` will be executed before the code in the run method of the `counter`.
Think of it this way:
- invokeLater starts a thread and that thread is schedules an asynchronous event on the EDT which is tasked with creating the `JLabel`.
- the counter is a separate thread that depends on the `JLabel` to exists so it can call `label.setText("You have " + i + " seconds.");`
Now you have a race condition: `JLabel` must be created BEFORE the `counter` thread starts, if it's not created before the counter thread starts, then your counter thread will be calling `setText` on an uninitialized object.
In order to ensure that the race condition is eliminated we must guarantee the order of execution and one way to guarantee it is to execute `showGUI()` and `counter.start()` sequentially on the same thread:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
showGUI();
counter.start();
}
});
}
Now `showGUI();` and `counter.start();` are executed from the same thread, thus the `JLabel` will be created before the `counter` is started.
Update:
Q: And I do not understand what is special about this thread. A: Swing event handling code runs on a special thread known as the event dispatch thread. Most code that invokes Swing methods also runs on this thread. This is necessary because most Swing object methods are not "thread safe": invoking them from multiple threads risks thread interference or memory consistency errors. 1
Q: So, if we have a GUI why should we start it in a separate thread? A: There is probably a better answer than mine, but if you want to update the GUI from the EDT (which you do), then you have to start it from the EDT.
Q: And why we cannot just start the thread like any other other thread? A: See previous answer.
Q: Why we use some invokeLater and why this thread (EDT) start to execute request when it's ready. Why it is not always ready? A: The EDT might have some other AWT events it has to process. `invokeLater` Causes doRun.run() to be executed asynchronously on the AWT event dispatching thread. This will happen after all pending AWT events have been processed. This method should be used when an application thread needs to update the GUI. 2
Problem
With the help of people on stackoverflow I was able to get the following working code of a simple GUI countdown (which just displays a window counting down seconds). My main problem with this code is the `invokeLater` stuff. As far as I understand `invokeLater`, it sends a task to the event dispatching thread (EDT) and then the EDT executes this task whenever it "can" (whatever that means). Is that right? To my understanding, the code works like this: In the `main` method we use `invokeLater` to show the window (`showGUI` method). In other words, the code displaying the window will be executed in the EDT. In the `main` method we also start the `counter` and the counter (by construction) is executed in another thread (so it is not in the event dispatching thread). Right? The `counter` is executed in a separate thread and periodically it calls `updateGUI`. `updateGUI` is supposed to update the GUI. And the GUI is working in the EDT. So, `updateGUI` should also be executed in the EDT. It is the reason why the code for the `updateGUI` is enclosed in `invokeLater`. Is that right? What is not clear to me is why we call the `counter` from the EDT. Anyway, it is not executed in the EDT. It starts immediately, a new thread and the `counter` is executed there. So, why can we not call the `counter` in the main method after the `invokeLater` block? ``` import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; public class CountdownNew { static JLabel label; // Method which defines the appearance of the window. public static void showGUI() { JFrame frame = new JFrame("Simple Countdown"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); label = new JLabel("Some Text"); frame.add(label); frame.pack(); frame.setVisible(true); } // Define a new thread in which the countdown is counting down. public static Thread counter = new Thread() { public void run() { for (int i=10; i>0; i=i-1) { updateGUI(i,label); try {Thread.sleep(1000);} catch(InterruptedException e) {}; } } }; // A method which updates GUI (sets a new value of JLabel). private static void updateGUI(final int i, final JLabel label) { SwingUtilities.invokeLater( new Runnable() { public void run() { label.setText("You have " + i + " seconds."); } } ); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { showGUI(); counter.start(); } }); } } ```