Is Java capable of creating more than one EDT at a time?
I'm experimenting with setting up EDT and how it works in updating the content of a "heavy duty" panel with potentially a dozen of panels embedded inside and with hundreds of components altogether. Currently I have
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.update();
}
});
}
I've looked at the following posts:
Measuring "busyness" of the event dispatching thread
How does the event dispatch thread work?
Java Event-Dispatching Thread explanation
http://en.wiki2.org/wiki/Event_dispatching_thread
and so forth.
I sort of understand that if there are, say a dozen of events, that an single EDT has to handle, Java already has an internal scheduling mechanism to group/prioritize these events.
According to http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html
"This is necessary because most Swing object methods are not "thread safe": invoking them from multiple threads risks thread interference or memory consistency errors."
So what if I create a 2nd EDT with new Thread(new Runnable() { ... }.start() below?
Will java automatically merge the two EDTs back to one for fear of thread safety?
new Thread(new Runnable() {
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.update();
}
});
}
}).start();
The following applies in case one uses the Sun toolkit. I am not sure about other Java implementations (have not tested it).
One can have multiple EDT threads provided the toolkit is initialized from multiple threads from separate thread groups.
PoC code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] argv) {
for (int i = 0; i < 5; i++) {
// the separate thread group is needed at least for the Sun toolkit
ThreadGroup tg = new ThreadGroup("Test Group " + i);
Thread t = new Thread(tg, new Runnable() {
@Override
public void run() {
startApp();
}
}, "Test " + i);
t.start();
}
}
private static void startApp() {
sun.awt.SunToolkit.createNewAppContext();
final JFrame frm = new JFrame(Thread.currentThread().getName()) {
@Override
public void setVisible(boolean b) {
super.setVisible(b);
System.out.println("Closed");
if (!b) dispose();
}
};
final JTextArea ta = new JTextArea();
frm.add(ta);
JButton btn = new JButton("Dialog");
// Showing a modal dialog will block only this frame
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frm, "Test message from " + frm.getTitle());
}
});
frm.add(btn, BorderLayout.SOUTH);
frm.setPreferredSize(new Dimension(300, 300));
frm.pack();
frm.show();
Thread t = new Thread(new Runnable() {
int i = 0;
@Override
public void run() {
try {
while (true) {
i++;
if (!frm.isVisible()) break;
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
try {
ta.getDocument().insertString(0, "Test " + i + " " +
Thread.currentThread().hashCode() + // This is to show that we are actually on a different thread as invokeAndWait is static and one may suspect a different behaviour
"\n", null);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
Thread.sleep(1000 + (int)(Math.random() * 500));
}
Thread.sleep(4000); // This is just to show that the deamon thread might not have ended before the toolkit calls System.exit
} catch (Throwable t) {
t.printStackTrace();
}
System.out.println("Thread " + Thread.currentThread().getName() + " exit");
}
});
t.setDaemon(true);
t.start();
}
}
The above code demonstrates opening 5 frames on separate EDTs. When each frame is closed and disposed the respective EDT ends. When all the frames are closed and disposed the app will exit (automatically).
Notes:
I use the above approach in order to measure the height of a wrap enabled JTextArea that is populated with a multitude of different texts that might take some time and during that time I do not want to block the main UI. The result (the measured heights) is used on the main UI.
Tested with Java 1.6.0_25 and 1.8.0_60.