Java Event-Dispatching Thread explanation
The event dispatch thread is a special thread that is managed by AWT. Basically, it is a thread that runs in an infinite loop, processing events.
The java.awt.EventQueue.invokeLater
and javax.swing.SwingUtilities.invokeLater
methods are a way to provide code that will run on the event queue. Writing a UI framework that is safe in a multithreading environment is very difficult so the AWT authors decided that they would only allow operations on GUI objects to occur on a single special thread. All event handlers will execute on this thread and all code that modifies the GUI should also operate on this thread.
Now AWT does not usually check that you are not issuing GUI commands from another thread (The WPF framework for C# does do this), meaning it's possible to write a lot of code and be pretty much agnostic to this and not run into any problems. But this can lead to undefined behavior, so the best thing to do, is to always ensure that GUI code runs on the event dispatch thread. invokeLater
provides a mechanism to do this.
A classic example is that you need to run a long running operation like downloading a file. So you launch a thread to perform this action then, when it is completed, you use invokeLater
to update the UI. If you didn't use invokeLater
and instead you just updated the UI directly, you might have a race condition and undefined behavior could occur.
Wikipedia has more information
Also, if you are curious why the AWT authors don't just make the toolkit multithreaded, here is a good article.
What is the event dispatching thread?
The event dispatching thread is the thread that handles all GUI events and manages your Swing GUI. It is started somewhere in the Swing code if you have any GUI in your program. The reason it is done behind the scenes is because of simplicity - you do not have to bother with starting and managing an extra thread by yourself.
Regarding the fact that you have to update your GUI with invokeLater()
it is because of concurrency issues. The GUI can be modified only from one thread because Swing is not thread safe(it is worth to note that most of toolkits are not thread safe, there is a nice article that gives some ideas why). This is why you have to submit all GUI updates to run on EDT.
You can read more on concurrency in Swing and event dispatching thread in Sun tutorial on concurrency in Swing. Also, if you would like to see how this could be done in a different way you might like to check out SWT toolkit. In SWT you have to manage EDT by yourself.
How do you use the Event Dispatch Thread?
The trick is that when swing calls you it will ALWAYS be in the EDT, so you don't have to worry about it.
However if you are in a timer or an action triggered by some other external event, your main thread or any other thread you've created then yes, you have to use invokeLater or invokeAndWait.
In other words, yes swing does do "it" automatically. Needing to use invokeXx is so rare that if swing were to do it internally it would waste too much time.
Many java programmers never figure this out and it can cause some pretty nasty hard-to-find problems with drawing your GUI. I do wish swing threw an exception when you called it not using the EDT--Java would have a better reputation when it came to professional GUIs if it did because there would be less crap out there.
Event Dispatching Thread synchronization
The approach works if a "Lock" class is used. Initially I had used a Boolean and that was not working since these lock objects are replaces by the assignments e.g.
created=true.
will create a new separate Boolean object so the created.notify() signals a different object and does not stop the wait in the main thread.
The Lock version works. I have changed my question's code back to the original wrong Boolean version to show the point.
package com.bitplan.test.common;
import java.awt.Frame;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import junit.framework.TestCase;
/**
* test the event dispatching thread handling
*
* @author wf
*
*/
public class TestEDT extends TestCase {
public static class Lock {
boolean value;
public Lock(boolean value) {
super();
this.value = value;
}
/**
* @return the value
*/
public boolean isTrue() {
return value;
}
/**
* @param value the value to set
*/
public void set(boolean value) {
this.value = value;
}
}
private JTextField field;
private Lock modified=new Lock(false);
private Lock created=new Lock(false);
/**
* test UI handling
*
* @throws InterruptedException
*/
public void testUI() throws InterruptedException {
// see
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
synchronized(created) {
while(!created.isTrue())
created.wait();
}
field.getDocument().addDocumentListener(new DocumentListener() {
public void flagModification() {
synchronized(modified) {
modified.set(true);
modified.notify();
}
}
public void insertUpdate(DocumentEvent e) {
flagModification();
}
@Override
public void removeUpdate(DocumentEvent e) {
flagModification();
}
@Override
public void changedUpdate(DocumentEvent e) {
flagModification();
}
});
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updateField("fieldcontent");
}
});
synchronized(modified) {
while(!modified.isTrue())
// http://stackoverflow.com/questions/2536692/a-simple-scenario-using-wait-and-notify-in-java?noredirect=1&lq=1
modified.wait();
}
}
/**
* update the field with the new content;
*
* @param newContent
*/
protected void updateField(String newContent) {
field.setText(newContent);
}
/**
* create and show the given gui
*/
protected void createAndShowGUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setTitle("Example GUI");
JPanel panel = new JPanel();
field = new JTextField(30);
panel.add(field);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
synchronized(created) {
created.set(true);
created.notify();
}
}
}
event dispatching thread
The call to SwingUtilities.invokeLater(...)
it made at the initial stage of an application so that the creation of the UI occurs in the Event Dispatch Thread. It need to happen before the constructor or any subsequent method within the application is called. The thread created is known as an initial thread.
How do I know if I'm on the event dispatch thread?
- No.
- No.
- Background thread.
If code running outside the EDT calls a method defined in a GUI class, that code will not be run on the EDT but in the calling thread.
If code running in the EDT calls code defined in a non-GUI class, that code will run on the EDT.
The rule is that if you're not creating a different thread, the method you're calling will run on the thread the calling code is running from – threads do not correspond to what classes methods are defined in.
Methods that will run on the EDT are event listeners, when they're called by Swing – not by you. (They still might be if you're calling them from the EDT though.)
Also, any code inside the Runnable.run()
method passed to SwingUtilities.invokeLater()
and invokeAndWait()
is also run on the EDT.
Any normal methods you call from the EDT will run on the EDT.
Any code called from a Thread
you create (whether using threads directly, or ExecutorService
, or SwingWorker.doInBackground()
) is not on the EDT. Your program's main()
method is also not on the EDT.
How does the event dispatch thread work?
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 isschedules an asynchronous event on the EDT which is tasked with creating theJLabel
. - the counter is a separate thread that depends on the
JLabel
to exists so it can calllabel.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. 1Q: 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
How to judge if the current thread is the event dispatching thead
Use javax.swing.SwingUtilities.isEventDispatchThread()
or java awt.EventQueue.isDispatchThread()
(as already using EventQueue
)
From documentation of SwingUtilities
1
Returns true if the current thread is an AWT event dispatching thread.
As of 1.3 this method is just a cover for java.awt.EventQueue.isDispatchThread().
1: I prefer SwingUtilities
over EventQueue
even for invokeLater
(at least when using Swing)
Related Topics
How to Upload File Using Selenium Webdriver in Java
How to Tell If I'm Running in 64-Bit Jvm or 32-Bit Jvm (From Within a Program)
Scanner VS. Stringtokenizer VS. String.Split
How to Read a Specific Line Using the Specific Line Number from a File in Java
How to Parse a Mathematical Expression Given as a String and Return a Number
Java Securityexception: Signer Information Does Not Match
Initialising a Multidimensional Array in Java
Parse String to Date with Different Format in Java
Invalidkeyexception Illegal Key Size
How to Turn Off the Eclipse Code Formatter for Certain Sections of Java Code
How Does a Arraylist's Contains() Method Evaluate Objects
Java: Define Terms Initialization, Declaration and Assignment
Java Code for Calculating Leap Year